@adonix.org/cloud-spark 0.0.167 → 0.0.169

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -179,19 +179,6 @@ interface ErrorJson {
179
179
  details: string;
180
180
  }
181
181
 
182
- /**
183
- * Initialization options for an OctetStream response.
184
- *
185
- * @property size - Total size of the data (required).
186
- * @property offset - Start of the byte range (optional, defaults to 0).
187
- * @property length - Length of the byte range (optional, defaults to size).
188
- */
189
- interface OctetStreamInit {
190
- size: number;
191
- offset?: number;
192
- length?: number;
193
- }
194
-
195
182
  /**
196
183
  * Represents the constructor of a Worker subclass.
197
184
  *
@@ -237,6 +224,53 @@ interface Worker {
237
224
  getAllowedMethods(): Method[];
238
225
  }
239
226
 
227
+ /**
228
+ * Middleware interface for request/response processing.
229
+ *
230
+ * Middleware objects implement logic that can process requests and responses
231
+ * in a chainable manner. Each middleware receives a `Worker` object and a
232
+ * `next` function that invokes the next middleware in the chain.
233
+ *
234
+ * Implementers **must provide** the `handle` method.
235
+ *
236
+ * Example implementation:
237
+ * ```ts
238
+ * class LoggingMiddleware implements Middleware {
239
+ * public async handle(worker: Worker, next: () => Promise<Response>): Promise<Response> {
240
+ * console.log(`Processing request: ${worker.request.url}`);
241
+ * const response = await next();
242
+ * console.log(`Response status: ${response.status}`);
243
+ * return response;
244
+ * }
245
+ * }
246
+ * ```
247
+ */
248
+ interface Middleware {
249
+ /**
250
+ * Process a request in the middleware chain.
251
+ *
252
+ * @param worker - The `Worker` instance representing the request context.
253
+ * @param next - Function to invoke the next middleware in the chain.
254
+ * Must be called to continue the chain unless the middleware
255
+ * terminates early (e.g., returns a response directly).
256
+ * @returns A `Response` object, either returned directly or from `next()`.
257
+ */
258
+ handle(worker: Worker, next: () => Promise<Response>): Promise<Response>;
259
+ }
260
+
261
+ /**
262
+ * Initialization options for an OctetStream response.
263
+ *
264
+ * @property size - Total size of the data (required).
265
+ * @property offset - Start of the byte range (optional, defaults to 0).
266
+ * @property length - Length of the byte range (optional, defaults to size).
267
+ */
268
+ interface OctetStreamInit {
269
+ size: number;
270
+ offset?: number;
271
+ length?: number;
272
+ }
273
+
240
274
  /**
241
275
  * Parameters extracted from a matched route.
242
276
  *
@@ -312,40 +346,6 @@ type RouteTuple = [Method, string, RouteHandler];
312
346
  */
313
347
  type RouteTable = Iterable<RouteTuple>;
314
348
 
315
- /**
316
- * Abstract base class for middleware.
317
- *
318
- * Middleware classes implement request/response processing logic in a
319
- * chainable manner. Each middleware receives a `Worker` object and a
320
- * `next` function that invokes the next middleware in the chain.
321
- *
322
- * Subclasses **must implement** the `handle` method.
323
- *
324
- * Example subclass:
325
- * ```ts
326
- * class LoggingMiddleware extends Middleware {
327
- * public async handle(worker: Worker, next: () => Promise<Response>): Promise<Response> {
328
- * console.log(`Processing request: ${worker.request.url}`);
329
- * const response = await next();
330
- * console.log(`Response status: ${response.status}`);
331
- * return response;
332
- * }
333
- * }
334
- * ```
335
- */
336
- declare abstract class Middleware {
337
- /**
338
- * Process a request in the middleware chain.
339
- *
340
- * @param worker - The `Worker` instance representing the request context.
341
- * @param next - Function to invoke the next middleware in the chain.
342
- * Must be called to continue the chain unless the middleware
343
- * terminates early (e.g., returns a response directly).
344
- * @returns A `Response` object, either returned directly or from `next()`.
345
- */
346
- abstract handle(worker: Worker, next: () => Promise<Response>): Promise<Response>;
347
- }
348
-
349
349
  /**
350
350
  * Creates a Vary-aware caching middleware for Workers.
351
351
  *
@@ -362,6 +362,7 @@ declare abstract class Middleware {
362
362
  * @returns A `Middleware` instance that can be used in a Worker pipeline.
363
363
  */
364
364
  declare function cache(cacheName?: string, getKey?: (request: Request) => URL): Middleware;
365
+
365
366
  /**
366
367
  * Returns a new URL with its query parameters sorted into a stable order.
367
368
  *
@@ -533,7 +534,7 @@ declare abstract class BaseWorker implements Worker {
533
534
  static ignite<W extends Worker>(this: WorkerClass<W>): FetchHandler;
534
535
  }
535
536
 
536
- /** Internal base worker for handling middleware chains. */
537
+ /** Base worker for handling middleware chains. */
537
538
  declare abstract class MiddlewareWorker extends BaseWorker {
538
539
  /** Middleware handlers registered for this worker. */
539
540
  protected readonly middlewares: Middleware[];
@@ -542,15 +543,15 @@ declare abstract class MiddlewareWorker extends BaseWorker {
542
543
  */
543
544
  protected init(): void | Promise<void>;
544
545
  /**
545
- * Add a middleware instance to this worker.
546
+ * Add one or more middleware instances to this worker.
546
547
  *
547
548
  * The middleware will run for every request handled by this worker,
548
549
  * in the order they are added.
549
550
  *
550
- * @param handler - The middleware to run.
551
+ * @param middleware - One or more middleware instances to run.
551
552
  * @returns `this` to allow chaining multiple `.use()` calls.
552
553
  */
553
- use(handler: Middleware): this;
554
+ use(...middleware: Middleware[]): this;
554
555
  /**
555
556
  * Executes the middleware chain and dispatches the request.
556
557
  *
@@ -726,6 +727,12 @@ declare abstract class WorkerResponse extends CacheResponse {
726
727
  declare class ClonedResponse extends WorkerResponse {
727
728
  constructor(response: Response, cache?: CacheControl);
728
729
  }
730
+ /**
731
+ * Copies the response, but with null body and status 304 Not Modified.
732
+ */
733
+ declare class NotModified extends ClonedResponse {
734
+ constructor(response: Response);
735
+ }
729
736
  /**
730
737
  * Represents a successful response with customizable body, cache and status.
731
738
  */
@@ -800,8 +807,16 @@ declare class OctetStream extends WorkerResponse {
800
807
  /**
801
808
  * A streaming response for Cloudflare R2 objects.
802
809
  *
810
+ * **Partial content support:** To enable HTTP 206 streaming, you must provide
811
+ * request headers containing the `Range` header when calling the R2 bucket's `get()` method.
812
+ *
813
+ * Example:
814
+ * ```ts
815
+ * const stream = await this.env.R2_BUCKET.get("key", { range: this.request.headers });
816
+ * ```
817
+ *
803
818
  * @param source - The R2 object to stream.
804
- * @param cache - Optional caching information.
819
+ * @param cache - Optional caching override.
805
820
  */
806
821
  declare class R2ObjectStream extends OctetStream {
807
822
  constructor(source: R2ObjectBody, cache?: CacheControl);
@@ -878,6 +893,10 @@ declare class NotFound extends HttpError {
878
893
  declare class MethodNotAllowed extends HttpError {
879
894
  constructor(worker: Worker);
880
895
  }
896
+ /** 412 Precondition Failed error response */
897
+ declare class PreconditionFailed extends WorkerResponse {
898
+ constructor();
899
+ }
881
900
  /** 426 Upgrade Required error response. */
882
901
  declare class UpgradeRequired extends HttpError {
883
902
  constructor();
@@ -899,4 +918,4 @@ declare class ServiceUnavailable extends HttpError {
899
918
  constructor(details?: string);
900
919
  }
901
920
 
902
- export { BadRequest, BasicWorker, CacheControl, ClonedResponse, type CorsConfig, type CorsInit, DELETE, type ErrorJson, Forbidden, GET, HEAD, Head, HtmlResponse, HttpError, InternalServerError, JsonResponse, type MatchedRoute, Method, MethodNotAllowed, MethodNotImplemented, NotFound, NotImplemented, OPTIONS, OctetStream, type OctetStreamInit, Options, PATCH, POST, PUT, type PathParams, R2ObjectStream, type Route, type RouteCallback, type RouteHandler, type RouteTable, type RouteTuple, RouteWorker, ServiceUnavailable, SuccessResponse, TextResponse, Time, Unauthorized, UpgradeRequired, WebSocketSessions, WebSocketUpgrade, type Worker, type WorkerClass, WorkerResponse, cache, cors, sortSearchParams, stripSearchParams, websocket };
921
+ export { BadRequest, BasicWorker, CacheControl, ClonedResponse, type CorsConfig, type CorsInit, DELETE, type ErrorJson, Forbidden, GET, HEAD, Head, HtmlResponse, HttpError, InternalServerError, JsonResponse, type MatchedRoute, Method, MethodNotAllowed, MethodNotImplemented, type Middleware, NotFound, NotImplemented, NotModified, OPTIONS, OctetStream, type OctetStreamInit, Options, PATCH, POST, PUT, type PathParams, PreconditionFailed, R2ObjectStream, type Route, type RouteCallback, type RouteHandler, type RouteTable, type RouteTuple, RouteWorker, ServiceUnavailable, SuccessResponse, TextResponse, Time, Unauthorized, UpgradeRequired, WebSocketSessions, WebSocketUpgrade, type Worker, type WorkerClass, WorkerResponse, cache, cors, sortSearchParams, stripSearchParams, websocket };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import{StatusCodes as h}from"http-status-codes";import pe from"cache-control-parser";var L={parse:pe.parse,stringify:pe.stringify,DISABLE:Object.freeze({"no-cache":!0,"no-store":!0,"must-revalidate":!0,"max-age":0})};var M=(u=>(u.GET="GET",u.PUT="PUT",u.HEAD="HEAD",u.POST="POST",u.PATCH="PATCH",u.DELETE="DELETE",u.OPTIONS="OPTIONS",u))(M||{}),{GET:f,PUT:kt,HEAD:D,POST:Mt,PATCH:Dt,DELETE:Ht,OPTIONS:b}=M;var de={Second:1,Minute:60,Hour:3600,Day:86400,Week:604800,Month:2592e3,Year:31536e3};var m=class{};function H(t){return Array.isArray(t)&&t.every(e=>typeof e=="string")}function x(t){return typeof t=="string"}function le(t){return typeof t=="function"}function E(t){return typeof t=="number"&&!Number.isNaN(t)}function he(t){return typeof t=="boolean"}function me(t){if(t!==void 0&&!x(t))throw new TypeError("Cache name must be a string.")}function fe(t){if(t!==void 0&&!le(t))throw new TypeError("getKey must be a function.")}function Ce(t){if(!(t instanceof URL))throw new TypeError("getKey must return a URL.")}import{StatusCodes as Ze}from"http-status-codes";var n;(a=>(a.ACCEPT_ENCODING="Accept-Encoding",a.ACCEPT_RANGES="Accept-Ranges",a.ALLOW="Allow",a.CACHE_CONTROL="Cache-Control",a.CONNECTION="Connection",a.CONTENT_DISPOSITION="Content-Disposition",a.CONTENT_ENCODING="Content-Encoding",a.CONTENT_LANGUAGE="Content-Language",a.CONTENT_LENGTH="Content-Length",a.CONTENT_RANGE="Content-Range",a.CONTENT_TYPE="Content-Type",a.CONTENT_MD5="Content-MD5",a.ETAG="ETag",a.ORIGIN="Origin",a.VARY="Vary",a.ACCESS_CONTROL_ALLOW_CREDENTIALS="Access-Control-Allow-Credentials",a.ACCESS_CONTROL_ALLOW_HEADERS="Access-Control-Allow-Headers",a.ACCESS_CONTROL_ALLOW_METHODS="Access-Control-Allow-Methods",a.ACCESS_CONTROL_ALLOW_ORIGIN="Access-Control-Allow-Origin",a.ACCESS_CONTROL_EXPOSE_HEADERS="Access-Control-Expose-Headers",a.ACCESS_CONTROL_MAX_AGE="Access-Control-Max-Age",a.SEC_WEBSOCKET_VERSION="Sec-WebSocket-Version",a.UPGRADE="Upgrade"))(n||={});var Ee=[n.CONTENT_TYPE,n.CONTENT_LENGTH,n.CONTENT_RANGE,n.CONTENT_ENCODING,n.CONTENT_LANGUAGE,n.CONTENT_DISPOSITION,n.CONTENT_MD5],Se=[n.CONTENT_LENGTH,n.CONTENT_RANGE];function S(t,e){return t<e?-1:t>e?1:0}function d(t,e,r){let s=Array.isArray(r)?r:[r],o=Array.from(new Set(s.map(i=>i.trim()))).filter(i=>i.length).sort(S);if(!o.length){t.delete(e);return}t.set(e,o.join(", "))}function G(t,e,r){let s=Array.isArray(r)?r:[r];if(s.length===0)return;let i=R(t,e).concat(s.map(c=>c.trim()));d(t,e,i)}function R(t,e){let r=t.get(e)?.split(",").map(s=>s.trim()).filter(s=>s.length>0)??[];return Array.from(new Set(r)).sort(S)}function Q(t,e){for(let r of e)t.delete(r)}var et="https://vary";function Oe(t){return!(t.status!==Ze.OK||Z(t).includes("*"))}function Z(t){let e=R(t.headers,n.VARY);return Array.from(new Set(e.map(r=>r.toLowerCase()))).sort(S)}function ee(t){return t.map(e=>e.toLowerCase()).filter(e=>e!==n.ACCEPT_ENCODING.toLowerCase())}function te(t,e,r){let s=[],o=ee(e);o.sort(S);for(let c of o){let u=t.headers.get(c);u!==null&&s.push([c,u])}let i=tt(JSON.stringify([r.toString(),s]));return new URL(i,et).href}function tt(t){let e=new TextEncoder().encode(t),r="";for(let s of e)r+=String.fromCodePoint(s);return btoa(r).replaceAll("+","-").replaceAll("/","_").replace(/={1,2}$/,"")}function dr(t,e){return me(t),fe(e),new re(t,e)}function rt(t){let e=new URL(t.url),r=new URLSearchParams([...e.searchParams.entries()].sort(([s],[o])=>S(s,o)));return e.search=r.toString(),e.hash="",e}function lr(t){let e=new URL(t.url);return e.search="",e.hash="",e}var re=class extends m{constructor(r,s){super();this.cacheName=r;this.getKey=s;this.cacheName=r?.trim()||void 0}async handle(r,s){if(r.request.method!==f)return s();let o=this.cacheName?await caches.open(this.cacheName):caches.default,i=await this.getCached(o,r.request);if(i)return i;let c=await s();return this.setCached(o,r,c),c}async getCached(r,s){let o=this.getCacheKey(s),i=await r.match(o);if(!i)return;let c=this.getFilteredVary(i);if(c.length===0)return i;let u=te(s,c,o);return r.match(u)}async setCached(r,s,o){if(!Oe(o))return;let i=this.getCacheKey(s.request);s.ctx.waitUntil(r.put(i,o.clone()));let c=this.getFilteredVary(o);c.length>0&&s.ctx.waitUntil(r.put(te(s.request,c,i),o.clone()))}getFilteredVary(r){return ee(Z(r))}getCacheKey(r){let s=this.getKey?this.getKey(r):rt(r);return Ce(s),s.hash="",s}};var st=new Set(Object.values(M));function se(t){return x(t)&&st.has(t)}function ot(t){return Array.isArray(t)&&t.every(se)}function N(t){if(!ot(t)){let e=Array.isArray(t)?JSON.stringify(t):String(t);throw new TypeError(`Invalid method array: ${e}`)}}import{getReasonPhrase as nt,StatusCodes as p}from"http-status-codes";var v="utf-8";function I(t,e){return!e||t.toLowerCase().includes("charset=")?t:`${t}; charset=${e.toLowerCase()}`}function ge(t){if(typeof t!="object"||t===null)throw new TypeError("OctetStreamInit must be an object.");let e=t,r=e.size;if(!E(r)||r<0||!Number.isInteger(r))throw new RangeError(`OctetStreamInit.size must be a non-negative integer (size=${JSON.stringify(r)}).`);let s=e.offset??0;if(!E(s)||s<0||s>r||!Number.isInteger(s))throw new RangeError(`OctetStreamInit.offset must be a non-negative integer less than or equal to size (size=${JSON.stringify(r)}, offset=${JSON.stringify(s)}).`);let o=e.length??r-s;if(!E(o)||o<0||s+o>r||!Number.isInteger(o))throw new RangeError(`OctetStreamInit.length must be a non-negative integer less than or equal to size - offset (size=${JSON.stringify(r)}, offset=${JSON.stringify(s)}, length=${JSON.stringify(o)}).`)}var oe=class{headers=new Headers;status=p.OK;statusText;webSocket=null;mediaType=I("text/plain",v);get responseInit(){return{headers:this.headers,status:this.status,statusText:this.statusText??nt(this.status),webSocket:this.webSocket,encodeBody:"automatic"}}setHeader(e,r){d(this.headers,e,r)}mergeHeader(e,r){G(this.headers,e,r)}addContentType(){this.headers.get(n.CONTENT_TYPE)||this.setHeader(n.CONTENT_TYPE,this.mediaType)}filterHeaders(){this.status===p.NO_CONTENT?Q(this.headers,Se):this.status===p.NOT_MODIFIED&&Q(this.headers,Ee)}},ne=class extends oe{constructor(r){super();this.cache=r}addCacheHeader(){this.cache&&this.setHeader(n.CACHE_CONTROL,L.stringify(this.cache))}},O=class extends ne{constructor(r=null,s){super(s);this.body=r}async response(){this.addCacheHeader();let r=[p.NO_CONTENT,p.NOT_MODIFIED].includes(this.status)?null:this.body;return r&&this.addContentType(),this.filterHeaders(),new Response(r,this.responseInit)}},U=class extends O{constructor(e,r){let s=e.clone();super(s.body,r),this.headers=new Headers(s.headers),this.status=s.status,this.statusText=s.statusText}},W=class extends O{constructor(e=null,r,s=p.OK){super(e,r),this.status=s}},q=class extends W{constructor(e={},r,s=p.OK){super(JSON.stringify(e),r,s),this.mediaType=I("application/json",v)}},Ae=class extends W{constructor(e,r,s=p.OK,o=v){super(e,r,s),this.mediaType=I("text/html",o)}},xe=class extends W{constructor(e,r,s=p.OK,o=v){super(e,r,s),this.mediaType=I("text/plain",o)}},ie=class t extends O{constructor(e,r,s){ge(r),super(e,s),this.mediaType="application/octet-stream";let o=t.normalizeInit(r),{size:i,offset:c,length:u}=o;t.isPartial(o)&&(this.setHeader(n.CONTENT_RANGE,`bytes ${c}-${c+u-1}/${i}`),this.status=p.PARTIAL_CONTENT),this.setHeader(n.ACCEPT_RANGES,"bytes"),this.setHeader(n.CONTENT_LENGTH,`${u}`)}static normalizeInit(e){let{size:r}=e,s=e.offset??0,o=e.length??r-s;return s===0&&o===0&&r>0&&(o=1),{size:r,offset:s,length:o}}static isPartial(e){return e.size===0?!1:!(e.offset===0&&e.length===e.size)}},Te=class t extends ie{constructor(e,r){let s=r;!s&&e.httpMetadata?.cacheControl&&(s=L.parse(e.httpMetadata.cacheControl)),super(e.body,t.computeRange(e.size,e.range),s),this.setHeader(n.ETAG,e.httpEtag),e.httpMetadata?.contentType&&(this.mediaType=e.httpMetadata.contentType)}static computeRange(e,r){if(!r)return{size:e};if("suffix"in r){let s=Math.max(0,e-r.suffix),o=e-s;return{size:e,offset:s,length:o}}return{size:e,...r}}},be=class extends O{constructor(e){super(null),this.status=p.SWITCHING_PROTOCOLS,this.webSocket=e}},V=class extends O{constructor(e){super(),this.status=e.status,this.statusText=e.statusText,this.headers=new Headers(e.headers)}},y=class extends O{constructor(){super(),this.status=p.NO_CONTENT}};var K="*",Re=new Set([f,D,b]),Ne=[h.SWITCHING_PROTOCOLS,h.CONTINUE,h.PROCESSING,h.EARLY_HINTS,h.MOVED_PERMANENTLY,h.MOVED_TEMPORARILY,h.SEE_OTHER,h.TEMPORARY_REDIRECT,h.PERMANENT_REDIRECT],ye={allowedOrigins:[K],allowedHeaders:[n.CONTENT_TYPE],exposedHeaders:[],allowCredentials:!1,maxAge:5*de.Minute};async function _e(t,e){let r=new y,s=Pe(t.request);return s&&(Le(r.headers,e,s),ve(r.headers,e,s)),it(r.headers,t),ct(r.headers,e),at(r.headers,e),r.response()}async function we(t,e,r){let s=new U(t),o=Pe(e.request);return pt(s.headers),o&&(Le(s.headers,r,o),ve(s.headers,r,o),ut(s.headers,r)),s.response()}function Le(t,e,r){Ie(e)?d(t,n.ACCESS_CONTROL_ALLOW_ORIGIN,K):(e.allowedOrigins.includes(r)&&d(t,n.ACCESS_CONTROL_ALLOW_ORIGIN,r),G(t,n.VARY,n.ORIGIN))}function ve(t,e,r){e.allowCredentials&&(Ie(e)||e.allowedOrigins.includes(r)&&d(t,n.ACCESS_CONTROL_ALLOW_CREDENTIALS,"true"))}function it(t,e){let r=e.getAllowedMethods();N(r);let s=r.filter(o=>!Re.has(o));s.length>0&&d(t,n.ACCESS_CONTROL_ALLOW_METHODS,s)}function at(t,e){let r=Math.max(0,Math.floor(e.maxAge));d(t,n.ACCESS_CONTROL_MAX_AGE,String(r))}function ct(t,e){e.allowedHeaders.length>0&&d(t,n.ACCESS_CONTROL_ALLOW_HEADERS,e.allowedHeaders)}function ut(t,e){d(t,n.ACCESS_CONTROL_EXPOSE_HEADERS,e.exposedHeaders)}function Ie(t){return t.allowedOrigins.includes(K)}function pt(t){t.delete(n.ACCESS_CONTROL_MAX_AGE),t.delete(n.ACCESS_CONTROL_ALLOW_ORIGIN),t.delete(n.ACCESS_CONTROL_ALLOW_HEADERS),t.delete(n.ACCESS_CONTROL_ALLOW_METHODS),t.delete(n.ACCESS_CONTROL_EXPOSE_HEADERS),t.delete(n.ACCESS_CONTROL_ALLOW_CREDENTIALS)}function We(t){let{status:e,headers:r}=t;return!!(Ne.includes(e)||r.has(n.UPGRADE))}function Pe(t){let e=t.headers.get(n.ORIGIN)?.trim();if(!e||e==="null")return null;try{return new URL(e).origin}catch{return null}}function ke(t){if(t===void 0)return;if(typeof t!="object"||t===null)throw new TypeError("CorsInit must be an object.");let e=t;if(e.allowedOrigins!==void 0&&!H(e.allowedOrigins))throw new TypeError("CorsInit.allowedOrigins must be a string array.");if(e.allowedHeaders!==void 0&&!H(e.allowedHeaders))throw new TypeError("CorsInit.allowedHeaders must be a string array.");if(e.exposedHeaders!==void 0&&!H(e.exposedHeaders))throw new TypeError("CorsInit.exposedHeaders must be a string array.");if(e.allowCredentials!==void 0&&!he(e.allowCredentials))throw new TypeError("CorsInit.allowCredentials must be a boolean.");if(e.maxAge!==void 0&&!E(e.maxAge))throw new TypeError("CorsInit.maxAge must be a number.")}function $r(t){return ke(t),new ae(t)}var ae=class extends m{config;constructor(e){super(),this.config={...ye,...e}}async handle(e,r){if(e.request.method===b)return _e(e,this.config);let s=await r();return We(s)?s:we(s,e,this.config)}};import{getReasonPhrase as dt,StatusCodes as C}from"http-status-codes";var Me="upgrade",De="websocket";var T={NORMAL:1e3,GOING_AWAY:1001,PROTOCOL_ERROR:1002,UNSUPPORTED_DATA:1003,NO_STATUS:1005,ABNORMAL:1006,INVALID_PAYLOAD:1007,POLICY_VIOLATION:1008,MESSAGE_TOO_BIG:1009,MISSING_EXTENSION:1010,INTERNAL_ERROR:1011,TLS_HANDSHAKE:1015},He=new Set([T.NO_STATUS,T.ABNORMAL,T.TLS_HANDSHAKE]);var l=class extends q{constructor(r,s){let o={status:r,error:dt(r),details:s??""};super(o,L.DISABLE,r);this.details=s}},P=class extends l{constructor(e){super(C.BAD_REQUEST,e)}},Ge=class extends l{constructor(e){super(C.UNAUTHORIZED,e)}},Ue=class extends l{constructor(e){super(C.FORBIDDEN,e)}},g=class extends l{constructor(e){super(C.NOT_FOUND,e)}},k=class extends l{constructor(e){let r=e.getAllowedMethods();N(r),super(C.METHOD_NOT_ALLOWED,`${e.request.method} method not allowed.`),this.setHeader(n.ALLOW,r)}},B=class extends l{constructor(){super(C.UPGRADE_REQUIRED),this.headers.set(n.SEC_WEBSOCKET_VERSION,"13")}},z=class extends l{constructor(e){super(C.INTERNAL_SERVER_ERROR,e)}},ce=class extends l{constructor(e){super(C.NOT_IMPLEMENTED,e)}},A=class extends ce{constructor(e){super(`${e.request.method} method not implemented.`)}},qe=class extends l{constructor(e){super(C.SERVICE_UNAVAILABLE,e)}};function Ke(t){return R(t,n.CONNECTION).some(e=>e.toLowerCase()===Me)}function Be(t){return R(t,n.UPGRADE).some(e=>e.toLowerCase()===De)}function ze(t){return t.get(n.SEC_WEBSOCKET_VERSION)?.trim()==="13"}function ls(t="/"){return new ue(t)}var ue=class extends m{constructor(r){super();this.path=r}handle(r,s){if(r.request.method!==f||this.getPath(r.request)!==this.path)return s();let o=r.request.headers;return Ke(o)?Be(o)?ze(o)?s():new B().response():new P("Missing or invalid Upgrade header").response():new P("Missing or invalid Connection header").response()}getPath(r){return new URL(r.url).pathname}};function mt(t){return t instanceof ArrayBuffer||ArrayBuffer.isView(t)}function Fe(t){return x(t)?t.length>0:mt(t)?t.byteLength>0:!1}function Ye(t){return E(t)?ft(t)&&!Ct(t)?t:T.NORMAL:T.NORMAL}function ft(t){return t>=T.NORMAL&&t<=4999}function Ct(t){return He.has(t)}function je(t){if(x(t))return t.replaceAll(/[^\x20-\x7E]/g,"").slice(0,123)}function $e(t){if(t===null||typeof t!="object")throw new TypeError("WebSocket attachment must be an object");try{JSON.stringify(t)}catch{throw new TypeError("WebSocket attachment is not serializable")}}var F=class t{server;static isCustomEvent(e){return["open","warn"].includes(e)}customListeners={};constructor(e){this.server=e}addEventListener(e,r,s){if(t.isCustomEvent(e)){let o=this.customListeners[e];o||(o=[],this.customListeners[e]=o),o.push(r)}else{let o=e==="close"?{...s,once:!0}:s;this.server.addEventListener(e,r,o)}}removeEventListener(e,r){if(t.isCustomEvent(e)){let s=this.customListeners[e];if(s){let o=s.indexOf(r);o!==-1&&s.splice(o,1)}}else this.server.removeEventListener(e,r)}dispatch(e,r,s=!1){let o=this.customListeners[e]?.slice()??[];s&&(this.customListeners[e]=[]);for(let i of o)i(r)}warn(e){this.dispatch("warn",{type:"warn",message:e})}open(){this.dispatch("open",new Event("open"),!0)}};var _=class extends F{accepted=!1;server;constructor(e){super(e),this.server=e,this.server.addEventListener("close",this.onclose)}send(e){if(this.isState(WebSocket.CONNECTING,WebSocket.CLOSED)){this.warn("Cannot send: WebSocket not open");return}if(!Fe(e)){this.warn("Cannot send: empty or invalid data");return}this.server.send(e)}get attachment(){return this.server.deserializeAttachment()??{}}attach(e){if(e!==void 0)if(e===null)this.server.serializeAttachment({});else{let s={...this.attachment,...e};$e(s),this.server.serializeAttachment(s)}}get readyState(){return this.accepted?this.server.readyState:WebSocket.CONNECTING}isState(...e){return e.includes(this.readyState)}close(e,r){this.server.removeEventListener("close",this.onclose),this.server.close(Ye(e),je(r))}onclose=e=>{this.close(e.code,e.reason)}};var Y=class extends _{client;constructor(){let e=new WebSocketPair,[r,s]=[e[0],e[1]];super(s),this.client=r}acceptWebSocket(e,r){return e.acceptWebSocket(this.server,r),this.ready()}accept(){return this.server.accept(),this.ready()}ready(){return this.accepted=!0,this.open(),this.client}};var j=class extends _{constructor(e){super(e),this.accepted=!0}accept(){throw new Error("Do not call accept() on restore")}acceptWebSocket(){throw new Error("Do not call acceptWebSocket() on restore")}};var Je=class{map=new Map;create(e){class r extends Y{constructor(c){super();this.sessions=c}accept(){return this.addEventListener("close",()=>this.sessions.unregister(this.server)),this.sessions.register(this.server,this),super.accept()}acceptWebSocket(c,u){return this.sessions.register(this.server,this),super.acceptWebSocket(c,u)}}let s=new r(this);return s.attach(e),s}restore(e){class r extends j{constructor(o,i){super(i),o.register(this.server,this)}}return new r(this,e)}restoreAll(e){let r=[];for(let s of e)r.push(this.restore(s));return r}get(e){return this.map.get(e)}values(){return this.map.values()}keys(){return this.map.keys()}close(e,r,s){let o=this.get(e);return o&&o.close(r,s),this.unregister(e)}*[Symbol.iterator](){yield*this.values()}register(e,r){this.map.set(e,r)}unregister(e){return this.map.delete(e)}};var w=class{constructor(e,r,s){this._request=e;this._env=r;this._ctx=s}get request(){return this._request}get env(){return this._env}get ctx(){return this._ctx}isAllowed(e){let r=this.getAllowedMethods();return N(r),se(e)&&r.includes(e)}create(e){let r=this.constructor;return new r(e,this.env,this.ctx)}async response(e,...r){return new e(...r).response()}static ignite(){return{fetch:(e,r,s)=>new this(e,r,s).fetch()}}};function Xe(t){if(!(t instanceof m))throw new TypeError("Handler must be a subclass of Middleware.")}var $=class extends w{middlewares=[];init(){}use(e){return Xe(e),this.middlewares.push(e),this}async fetch(){return await this.middlewares.reduceRight((r,s)=>()=>s.handle(this,r),()=>this.dispatch())()}};var J=class extends ${async fetch(){if(!this.isAllowed(this.request.method))return this.response(k,this);try{return await this.init(),await super.fetch()}catch(e){return console.error(e),this.response(z)}}async dispatch(){let e=this.request.method;return({GET:()=>this.get(),PUT:()=>this.put(),HEAD:()=>this.head(),POST:()=>this.post(),PATCH:()=>this.patch(),DELETE:()=>this.delete(),OPTIONS:()=>this.options()}[e]??(()=>this.response(k,this)))()}async get(){return this.response(A,this)}async put(){return this.response(A,this)}async post(){return this.response(A,this)}async patch(){return this.response(A,this)}async delete(){return this.response(A,this)}async options(){return this.response(y)}async head(){let e=this.create(new Request(this.request.url,{method:f,headers:this.request.headers}));return this.response(V,await e.fetch())}getAllowedMethods(){return[f,D,b]}};import{match as Et}from"path-to-regexp";var X=class{routes=[];add(e){for(let[r,s,o]of e){let i=Et(s);this.routes.push({method:r,matcher:i,handler:o})}}match(e,r){let s=new URL(r).pathname;for(let o of this){if(o.method!==e)continue;let i=o.matcher(s);if(i)return{route:o,params:i.params}}return null}*[Symbol.iterator](){yield*this.routes}};var Qe=class t extends J{_routes=new X;route(e,r,s){return this.routes([[e,r,s]]),this}routes(e){return this._routes.add(e),this}async dispatch(){let e=this._routes.match(this.request.method,this.request.url);if(!e)return super.dispatch();let{handler:r}=e.route;return t.isWorkerClass(r)?new r(this.request,this.env,this.ctx).fetch():r.call(this,e.params)}static isWorkerClass(e){return w.prototype.isPrototypeOf(e.prototype)}async get(){return this.response(g)}async put(){return this.response(g)}async post(){return this.response(g)}async patch(){return this.response(g)}async delete(){return this.response(g)}};export{P as BadRequest,J as BasicWorker,L as CacheControl,U as ClonedResponse,Ht as DELETE,Ue as Forbidden,f as GET,D as HEAD,V as Head,Ae as HtmlResponse,l as HttpError,z as InternalServerError,q as JsonResponse,M as Method,k as MethodNotAllowed,A as MethodNotImplemented,g as NotFound,ce as NotImplemented,b as OPTIONS,ie as OctetStream,y as Options,Dt as PATCH,Mt as POST,kt as PUT,Te as R2ObjectStream,Qe as RouteWorker,qe as ServiceUnavailable,h as StatusCodes,W as SuccessResponse,xe as TextResponse,de as Time,Ge as Unauthorized,B as UpgradeRequired,Je as WebSocketSessions,be as WebSocketUpgrade,O as WorkerResponse,dr as cache,$r as cors,rt as sortSearchParams,lr as stripSearchParams,ls as websocket};
1
+ import{StatusCodes as h}from"http-status-codes";import Oe from"cache-control-parser";var x={parse:Oe.parse,stringify:Oe.stringify,DISABLE:Object.freeze({"no-cache":!0,"no-store":!0,"must-revalidate":!0,"max-age":0})};var H=(u=>(u.GET="GET",u.PUT="PUT",u.HEAD="HEAD",u.POST="POST",u.PATCH="PATCH",u.DELETE="DELETE",u.OPTIONS="OPTIONS",u))(H||{}),{GET:d,PUT:tr,HEAD:G,POST:rr,PATCH:sr,DELETE:or,OPTIONS:b}=H;var Re={Second:1,Minute:60,Hour:3600,Day:86400,Week:604800,Month:2592e3,Year:31536e3};function U(t){return Array.isArray(t)&&t.every(e=>typeof e=="string")}function A(t){return typeof t=="string"}function xe(t){return typeof t=="function"}function f(t){return typeof t=="number"&&!Number.isNaN(t)}function Ae(t){return typeof t=="boolean"}function Te(t){if(t!==void 0&&!A(t))throw new TypeError("Cache name must be a string.")}function be(t){if(t!==void 0&&!xe(t))throw new TypeError("getKey must be a function.")}function Ne(t){if(!(t instanceof URL))throw new TypeError("getKey must return a URL.")}import{StatusCodes as ft}from"http-status-codes";var n;(c=>(c.ACCEPT_ENCODING="Accept-Encoding",c.ACCEPT_RANGES="Accept-Ranges",c.ALLOW="Allow",c.CACHE_CONTROL="Cache-Control",c.CONNECTION="Connection",c.CONTENT_DISPOSITION="Content-Disposition",c.CONTENT_ENCODING="Content-Encoding",c.CONTENT_LANGUAGE="Content-Language",c.CONTENT_LENGTH="Content-Length",c.CONTENT_RANGE="Content-Range",c.CONTENT_TYPE="Content-Type",c.CONTENT_MD5="Content-MD5",c.ETAG="ETag",c.IF_MODIFIED_SINCE="If-Modified-Since",c.IF_MATCH="If-Match",c.IF_NONE_MATCH="If-None-Match",c.LAST_MODIFIED="Last-Modified",c.ORIGIN="Origin",c.RANGE="Range",c.VARY="Vary",c.ACCESS_CONTROL_ALLOW_CREDENTIALS="Access-Control-Allow-Credentials",c.ACCESS_CONTROL_ALLOW_HEADERS="Access-Control-Allow-Headers",c.ACCESS_CONTROL_ALLOW_METHODS="Access-Control-Allow-Methods",c.ACCESS_CONTROL_ALLOW_ORIGIN="Access-Control-Allow-Origin",c.ACCESS_CONTROL_EXPOSE_HEADERS="Access-Control-Expose-Headers",c.ACCESS_CONTROL_MAX_AGE="Access-Control-Max-Age",c.SEC_WEBSOCKET_VERSION="Sec-WebSocket-Version",c.UPGRADE="Upgrade"))(n||={});var ye=[n.CONTENT_TYPE,n.CONTENT_LENGTH,n.CONTENT_RANGE,n.CONTENT_ENCODING,n.CONTENT_LANGUAGE,n.CONTENT_DISPOSITION,n.CONTENT_MD5],_e=[n.CONTENT_LENGTH,n.CONTENT_RANGE];function g(t,e){return t<e?-1:t>e?1:0}function l(t,e,r){let s=Array.isArray(r)?r:[r],o=Array.from(new Set(s.map(i=>i.trim()))).filter(i=>i.length).sort(g);if(!o.length){t.delete(e);return}t.set(e,o.join(", "))}function V(t,e,r){let s=Array.isArray(r)?r:[r];if(s.length===0)return;let i=S(t,e).concat(s.map(a=>a.trim()));l(t,e,i)}function S(t,e){let r=t.get(e)?.split(",").map(s=>s.trim()).filter(s=>s.length>0)??[];return Array.from(new Set(r)).sort(g)}function ae(t,e){for(let r of e)t.delete(r)}var Ct="https://vary";function we(t,e){return!(t.method!==d||t.cache==="no-store"||e.status!==ft.OK||ce(e).includes("*"))}function ce(t){let e=S(t.headers,n.VARY);return Array.from(new Set(e.map(r=>r.toLowerCase()))).sort(g)}function ue(t){return t.map(e=>e.toLowerCase()).filter(e=>e!==n.ACCEPT_ENCODING.toLowerCase())}function pe(t,e,r){let s=[],o=ue(e);o.sort(g);for(let a of o){let u=t.headers.get(a);u!==null&&s.push([a,u])}let i=Et(JSON.stringify([r.toString(),s]));return new URL(i,Ct).href}function Et(t){let e=new TextEncoder().encode(t),r="";for(let s of e)r+=String.fromCodePoint(s);return btoa(r).replaceAll("+","-").replaceAll("/","_").replace(/={1,2}$/,"")}import{StatusCodes as St}from"http-status-codes";var q=class{rules=[];use(...e){return this.rules.push(...e),this}async execute(e,r){return await this.rules.reduceRight((o,i)=>async()=>{let a=await o();if(a)return a.status!==St.OK?a:i.handle(e,async()=>a)},()=>r())()}};var K=class{async handle(e,r){if(e.request.method===d)return r()}};var gt=/^bytes=(\d{1,12})-(\d{0,12})$/;function Ie(t){let e=t.headers.get("range");if(!e)return;let r=gt.exec(e);if(!r)return;let s=Number(r[1]),o=r[2]===""?void 0:Number(r[2]);return o!==void 0?{start:s,end:o}:{start:s}}function de(t){return t.startsWith("W/")?t.slice(2):t}function Le(t){return t}function We(t){return x.parse(t.get(n.CACHE_CONTROL)??"")}function L(t){return{ifNoneMatch:S(t,n.IF_NONE_MATCH),ifMatch:S(t,n.IF_MATCH),ifModifiedSince:t.get(n.IF_MODIFIED_SINCE)}}function ve(t){let{ifNoneMatch:e,ifMatch:r,ifModifiedSince:s}=L(t);return e.length>0||r.length>0||s!==null}var B=class{async handle(e,r){let s=Ie(e.request);if(s&&(s.start!==0||s.end===0))return;let o=await r();if(!s||s.end===void 0)return o;let i=o.headers.get(n.CONTENT_LENGTH),a=Number(i);if(!(!f(a)||s.end>=a))return o}};import{getReasonPhrase as xt,StatusCodes as E}from"http-status-codes";import{getReasonPhrase as Ge,StatusCodes as p}from"http-status-codes";var W="utf-8";function v(t,e){return!e||t.toLowerCase().includes("charset=")?t:`${t}; charset=${e.toLowerCase()}`}function Me(t){if(typeof t!="object"||t===null)throw new TypeError("OctetStreamInit must be an object.");let e=t,r=e.size;if(!f(r)||r<0||!Number.isInteger(r))throw new RangeError(`OctetStreamInit.size must be a non-negative integer (size=${JSON.stringify(r)}).`);let s=e.offset??0;if(!f(s)||s<0||s>r||!Number.isInteger(s))throw new RangeError(`OctetStreamInit.offset must be a non-negative integer less than or equal to size (size=${JSON.stringify(r)}, offset=${JSON.stringify(s)}).`);let o=e.length??r-s;if(!f(o)||o<0||s+o>r||!Number.isInteger(o))throw new RangeError(`OctetStreamInit.length must be a non-negative integer less than or equal to size - offset (size=${JSON.stringify(r)}, offset=${JSON.stringify(s)}, length=${JSON.stringify(o)}).`)}var le=class{headers=new Headers;status=p.OK;statusText;webSocket=null;mediaType=v("text/plain",W);get responseInit(){return{headers:this.headers,status:this.status,statusText:this.statusText??Ge(this.status),webSocket:this.webSocket,encodeBody:"automatic"}}setHeader(e,r){l(this.headers,e,r)}mergeHeader(e,r){V(this.headers,e,r)}addContentType(){this.headers.get(n.CONTENT_TYPE)||this.setHeader(n.CONTENT_TYPE,this.mediaType)}filterHeaders(){this.status===p.NO_CONTENT?ae(this.headers,_e):this.status===p.NOT_MODIFIED&&ae(this.headers,ye)}},me=class extends le{constructor(r){super();this.cache=r}addCacheHeader(){this.cache&&this.setHeader(n.CACHE_CONTROL,x.stringify(this.cache))}},C=class extends me{constructor(r=null,s){super(s);this.body=r}async response(){this.addCacheHeader();let r=[p.NO_CONTENT,p.NOT_MODIFIED].includes(this.status)?null:this.body;return r&&this.addContentType(),this.filterHeaders(),new Response(r,this.responseInit)}},M=class extends C{constructor(e,r){let s=e.clone();super(s.body,r),this.headers=new Headers(s.headers),this.status=s.status,this.statusText=s.statusText}},N=class extends M{constructor(e){super(e),this.status=p.NOT_MODIFIED,this.statusText=Ge(p.NOT_MODIFIED)}},P=class extends C{constructor(e=null,r,s=p.OK){super(e,r),this.status=s}},F=class extends P{constructor(e={},r,s=p.OK){super(JSON.stringify(e),r,s),this.mediaType=v("application/json",W)}},Pe=class extends P{constructor(e,r,s=p.OK,o=W){super(e,r,s),this.mediaType=v("text/html",o)}},ke=class extends P{constructor(e,r,s=p.OK,o=W){super(e,r,s),this.mediaType=v("text/plain",o)}},he=class t extends C{constructor(e,r,s){Me(r),super(e,s),this.mediaType="application/octet-stream";let o=t.normalizeInit(r),{size:i,offset:a,length:u}=o;t.isPartial(o)&&(this.setHeader(n.CONTENT_RANGE,`bytes ${a}-${a+u-1}/${i}`),this.status=p.PARTIAL_CONTENT),this.setHeader(n.ACCEPT_RANGES,"bytes"),this.setHeader(n.CONTENT_LENGTH,`${u}`)}static normalizeInit(e){let{size:r}=e,s=e.offset??0,o=e.length??r-s;return s===0&&o===0&&r>0&&(o=1),{size:r,offset:s,length:o}}static isPartial(e){return e.size===0?!1:!(e.offset===0&&e.length===e.size)}},De=class t extends he{constructor(e,r){let s=r;!s&&e.httpMetadata?.cacheControl&&(s=x.parse(e.httpMetadata.cacheControl)),super(e.body,t.computeRange(e.size,e.range),s),this.setHeader(n.ETAG,e.httpEtag),e.httpMetadata?.contentType&&(this.mediaType=e.httpMetadata.contentType)}static computeRange(e,r){if(!r)return{size:e};if("suffix"in r){let s=Math.max(0,e-r.suffix),o=e-s;return{size:e,offset:s,length:o}}return{size:e,...r}}},He=class extends C{constructor(e){super(null),this.status=p.SWITCHING_PROTOCOLS,this.webSocket=e}},z=class extends C{constructor(e){super(),this.status=e.status,this.statusText=e.statusText,this.headers=new Headers(e.headers)}},y=class extends C{constructor(){super(),this.status=p.NO_CONTENT}};var Ot=new Set(Object.values(H));function fe(t){return A(t)&&Ot.has(t)}function Rt(t){return Array.isArray(t)&&t.every(fe)}function _(t){if(!Rt(t)){let e=Array.isArray(t)?JSON.stringify(t):String(t);throw new TypeError(`Invalid method array: ${e}`)}}var Ue="upgrade",Ve="websocket";var T={NORMAL:1e3,GOING_AWAY:1001,PROTOCOL_ERROR:1002,UNSUPPORTED_DATA:1003,NO_STATUS:1005,ABNORMAL:1006,INVALID_PAYLOAD:1007,POLICY_VIOLATION:1008,MESSAGE_TOO_BIG:1009,MISSING_EXTENSION:1010,INTERNAL_ERROR:1011,TLS_HANDSHAKE:1015},qe=new Set([T.NO_STATUS,T.ABNORMAL,T.TLS_HANDSHAKE]);var m=class extends F{constructor(r,s){let o={status:r,error:xt(r),details:s??""};super(o,x.DISABLE,r);this.details=s}},k=class extends m{constructor(e){super(E.BAD_REQUEST,e)}},Ke=class extends m{constructor(e){super(E.UNAUTHORIZED,e)}},Be=class extends m{constructor(e){super(E.FORBIDDEN,e)}},O=class extends m{constructor(e){super(E.NOT_FOUND,e)}},D=class extends m{constructor(e){let r=e.getAllowedMethods();_(r),super(E.METHOD_NOT_ALLOWED,`${e.request.method} method not allowed.`),this.setHeader(n.ALLOW,r)}},Y=class extends C{constructor(){super(),this.status=E.PRECONDITION_FAILED}},j=class extends m{constructor(){super(E.UPGRADE_REQUIRED),this.headers.set(n.SEC_WEBSOCKET_VERSION,"13")}},$=class extends m{constructor(e){super(E.INTERNAL_SERVER_ERROR,e)}},Ce=class extends m{constructor(e){super(E.NOT_IMPLEMENTED,e)}},R=class extends Ce{constructor(e){super(`${e.request.method} method not implemented.`)}},Fe=class extends m{constructor(e){super(E.SERVICE_UNAVAILABLE,e)}};var X=class{async handle(e,r){let s=await r(),o=s.headers.get(n.ETAG);if(!o)return s;let{ifNoneMatch:i,ifMatch:a}=L(e.request.headers);if(a.length>0&&!a.includes("*")){let u=o;if(!a.map(Le).includes(u))return new Y().response()}if(i.length>0){let u=de(o),ie=i.map(de);return i.includes("*")||ie.includes(u)?new N(s).response():void 0}return s}};var J=class{async handle(e,r){let s=await r(),o=s.headers.get(n.LAST_MODIFIED),{ifModifiedSince:i}=L(e.request.headers);if(!o||!i)return s;let a=Date.parse(o),u=Date.parse(i);return isNaN(a)||isNaN(u)?s:a<=u?new N(s).response():s}};var Q=class{async handle(e,r){let s=We(e.request.headers);if(!s["no-store"]&&!(s["no-cache"]&&!ve(e.request.headers)))return r()}};function Ye(t){let e=new URL(t.url),r=new URLSearchParams([...e.searchParams.entries()].sort(([s],[o])=>g(s,o)));return e.search=r.toString(),e.hash="",e}function gs(t){let e=new URL(t.url);return e.search="",e.hash="",e}function Is(t,e){return Te(t),be(e),new Ee(t,e)}var Ee=class{constructor(e,r){this.cacheName=e;this.getKey=r;this.cacheName=e?.trim()||void 0}async handle(e,r){let s=this.cacheName?await caches.open(this.cacheName):caches.default,i=await new q().use(new K).use(new Q).use(new B).use(new X).use(new J).execute(e,()=>this.getCached(s,e.request));if(i)return i;let a=await r();return this.setCached(s,e,a),a}async getCached(e,r){let s=this.getCacheKey(r),o=await e.match(s);if(!o)return;let i=this.getFilteredVary(o);if(i.length===0)return o;let a=pe(r,i,s);return e.match(a)}async setCached(e,r,s){if(!we(r.request,s))return;let o=this.getCacheKey(r.request);r.ctx.waitUntil(e.put(o,s.clone()));let i=this.getFilteredVary(s);i.length>0&&r.ctx.waitUntil(e.put(pe(r.request,i,o),s.clone()))}getFilteredVary(e){return ue(ce(e))}getCacheKey(e){let r=this.getKey?this.getKey(e):Ye(e);return Ne(r),r.hash="",r}};var Z="*",je=new Set([d,G,b]),$e=[h.SWITCHING_PROTOCOLS,h.CONTINUE,h.PROCESSING,h.EARLY_HINTS,h.MOVED_PERMANENTLY,h.MOVED_TEMPORARILY,h.SEE_OTHER,h.TEMPORARY_REDIRECT,h.PERMANENT_REDIRECT],Xe={allowedOrigins:[Z],allowedHeaders:[n.CONTENT_TYPE],exposedHeaders:[],allowCredentials:!1,maxAge:5*Re.Minute};async function Je(t,e){let r=new y,s=st(t.request);return s&&(Ze(r.headers,e,s),et(r.headers,e,s)),At(r.headers,t),bt(r.headers,e),Tt(r.headers,e),r.response()}async function Qe(t,e,r){let s=new M(t),o=st(e.request);return yt(s.headers),o&&(Ze(s.headers,r,o),et(s.headers,r,o),Nt(s.headers,r)),s.response()}function Ze(t,e,r){tt(e)?l(t,n.ACCESS_CONTROL_ALLOW_ORIGIN,Z):(e.allowedOrigins.includes(r)&&l(t,n.ACCESS_CONTROL_ALLOW_ORIGIN,r),V(t,n.VARY,n.ORIGIN))}function et(t,e,r){e.allowCredentials&&(tt(e)||e.allowedOrigins.includes(r)&&l(t,n.ACCESS_CONTROL_ALLOW_CREDENTIALS,"true"))}function At(t,e){let r=e.getAllowedMethods();_(r);let s=r.filter(o=>!je.has(o));s.length>0&&l(t,n.ACCESS_CONTROL_ALLOW_METHODS,s)}function Tt(t,e){let r=Math.max(0,Math.floor(e.maxAge));l(t,n.ACCESS_CONTROL_MAX_AGE,String(r))}function bt(t,e){e.allowedHeaders.length>0&&l(t,n.ACCESS_CONTROL_ALLOW_HEADERS,e.allowedHeaders)}function Nt(t,e){l(t,n.ACCESS_CONTROL_EXPOSE_HEADERS,e.exposedHeaders)}function tt(t){return t.allowedOrigins.includes(Z)}function yt(t){t.delete(n.ACCESS_CONTROL_MAX_AGE),t.delete(n.ACCESS_CONTROL_ALLOW_ORIGIN),t.delete(n.ACCESS_CONTROL_ALLOW_HEADERS),t.delete(n.ACCESS_CONTROL_ALLOW_METHODS),t.delete(n.ACCESS_CONTROL_EXPOSE_HEADERS),t.delete(n.ACCESS_CONTROL_ALLOW_CREDENTIALS)}function rt(t){let{status:e,headers:r}=t;return!!($e.includes(e)||r.has(n.UPGRADE))}function st(t){let e=t.headers.get(n.ORIGIN)?.trim();if(!e||e==="null")return null;try{return new URL(e).origin}catch{return null}}function ot(t){if(t===void 0)return;if(typeof t!="object"||t===null)throw new TypeError("CorsInit must be an object.");let e=t;if(e.allowedOrigins!==void 0&&!U(e.allowedOrigins))throw new TypeError("CorsInit.allowedOrigins must be a string array.");if(e.allowedHeaders!==void 0&&!U(e.allowedHeaders))throw new TypeError("CorsInit.allowedHeaders must be a string array.");if(e.exposedHeaders!==void 0&&!U(e.exposedHeaders))throw new TypeError("CorsInit.exposedHeaders must be a string array.");if(e.allowCredentials!==void 0&&!Ae(e.allowCredentials))throw new TypeError("CorsInit.allowCredentials must be a boolean.");if(e.maxAge!==void 0&&!f(e.maxAge))throw new TypeError("CorsInit.maxAge must be a number.")}function Xs(t){return ot(t),new Se(t)}var Se=class{config;constructor(e){this.config={...Xe,...e}}async handle(e,r){if(e.request.method===b)return Je(e,this.config);let s=await r();return rt(s)?s:Qe(s,e,this.config)}};function nt(t){return S(t,n.CONNECTION).some(e=>e.toLowerCase()===Ue)}function it(t){return S(t,n.UPGRADE).some(e=>e.toLowerCase()===Ve)}function at(t){return t.get(n.SEC_WEBSOCKET_VERSION)?.trim()==="13"}function no(t="/"){return new ge(t)}var ge=class{constructor(e){this.path=e}handle(e,r){if(e.request.method!==d||this.getPath(e.request)!==this.path)return r();let s=e.request.headers;return nt(s)?it(s)?at(s)?r():new j().response():new k("Missing or invalid Upgrade header").response():new k("Missing or invalid Connection header").response()}getPath(e){return new URL(e.url).pathname}};function It(t){return t instanceof ArrayBuffer||ArrayBuffer.isView(t)}function ct(t){return A(t)?t.length>0:It(t)?t.byteLength>0:!1}function ut(t){return f(t)?Lt(t)&&!Wt(t)?t:T.NORMAL:T.NORMAL}function Lt(t){return t>=T.NORMAL&&t<=4999}function Wt(t){return qe.has(t)}function pt(t){if(A(t))return t.replaceAll(/[^\x20-\x7E]/g,"").slice(0,123)}function dt(t){if(t===null||typeof t!="object")throw new TypeError("WebSocket attachment must be an object");try{JSON.stringify(t)}catch{throw new TypeError("WebSocket attachment is not serializable")}}var ee=class t{server;static isCustomEvent(e){return["open","warn"].includes(e)}customListeners={};constructor(e){this.server=e}addEventListener(e,r,s){if(t.isCustomEvent(e)){let o=this.customListeners[e];o||(o=[],this.customListeners[e]=o),o.push(r)}else{let o=e==="close"?{...s,once:!0}:s;this.server.addEventListener(e,r,o)}}removeEventListener(e,r){if(t.isCustomEvent(e)){let s=this.customListeners[e];if(s){let o=s.indexOf(r);o!==-1&&s.splice(o,1)}}else this.server.removeEventListener(e,r)}dispatch(e,r,s=!1){let o=this.customListeners[e]?.slice()??[];s&&(this.customListeners[e]=[]);for(let i of o)i(r)}warn(e){this.dispatch("warn",{type:"warn",message:e})}open(){this.dispatch("open",new Event("open"),!0)}};var w=class extends ee{accepted=!1;server;constructor(e){super(e),this.server=e,this.server.addEventListener("close",this.onclose)}send(e){if(this.isState(WebSocket.CONNECTING,WebSocket.CLOSED)){this.warn("Cannot send: WebSocket not open");return}if(!ct(e)){this.warn("Cannot send: empty or invalid data");return}this.server.send(e)}get attachment(){return this.server.deserializeAttachment()??{}}attach(e){if(e!==void 0)if(e===null)this.server.serializeAttachment({});else{let s={...this.attachment,...e};dt(s),this.server.serializeAttachment(s)}}get readyState(){return this.accepted?this.server.readyState:WebSocket.CONNECTING}isState(...e){return e.includes(this.readyState)}close(e,r){this.server.removeEventListener("close",this.onclose),this.server.close(ut(e),pt(r))}onclose=e=>{this.close(e.code,e.reason)}};var te=class extends w{client;constructor(){let e=new WebSocketPair,[r,s]=[e[0],e[1]];super(s),this.client=r}acceptWebSocket(e,r){return e.acceptWebSocket(this.server,r),this.ready()}accept(){return this.server.accept(),this.ready()}ready(){return this.accepted=!0,this.open(),this.client}};var re=class extends w{constructor(e){super(e),this.accepted=!0}accept(){throw new Error("Do not call accept() on restore")}acceptWebSocket(){throw new Error("Do not call acceptWebSocket() on restore")}};var lt=class{map=new Map;create(e){class r extends te{constructor(a){super();this.sessions=a}accept(){return this.addEventListener("close",()=>this.sessions.unregister(this.server)),this.sessions.register(this.server,this),super.accept()}acceptWebSocket(a,u){return this.sessions.register(this.server,this),super.acceptWebSocket(a,u)}}let s=new r(this);return s.attach(e),s}restore(e){class r extends re{constructor(o,i){super(i),o.register(this.server,this)}}return new r(this,e)}restoreAll(e){let r=[];for(let s of e)r.push(this.restore(s));return r}get(e){return this.map.get(e)}values(){return this.map.values()}keys(){return this.map.keys()}close(e,r,s){let o=this.get(e);return o&&o.close(r,s),this.unregister(e)}*[Symbol.iterator](){yield*this.values()}register(e,r){this.map.set(e,r)}unregister(e){return this.map.delete(e)}};var I=class{constructor(e,r,s){this._request=e;this._env=r;this._ctx=s}get request(){return this._request}get env(){return this._env}get ctx(){return this._ctx}isAllowed(e){let r=this.getAllowedMethods();return _(r),fe(e)&&r.includes(e)}create(e){let r=this.constructor;return new r(e,this.env,this.ctx)}async response(e,...r){return new e(...r).response()}static ignite(){return{fetch:(e,r,s)=>new this(e,r,s).fetch()}}};function mt(t){if(t===null||typeof t!="object"||typeof t.handle!="function")throw new TypeError("Handler must implement the Middleware interface (have a handle method).")}var se=class extends I{middlewares=[];init(){}use(...e){return e.forEach(mt),this.middlewares.push(...e),this}async fetch(){return await this.middlewares.reduceRight((r,s)=>()=>s.handle(this,r),()=>this.dispatch())()}};var oe=class extends se{async fetch(){if(!this.isAllowed(this.request.method))return this.response(D,this);try{return await this.init(),await super.fetch()}catch(e){return console.error(e),this.response($)}}async dispatch(){let e=this.request.method;return({GET:()=>this.get(),PUT:()=>this.put(),HEAD:()=>this.head(),POST:()=>this.post(),PATCH:()=>this.patch(),DELETE:()=>this.delete(),OPTIONS:()=>this.options()}[e]??(()=>this.response(D,this)))()}async get(){return this.response(R,this)}async put(){return this.response(R,this)}async post(){return this.response(R,this)}async patch(){return this.response(R,this)}async delete(){return this.response(R,this)}async options(){return this.response(y)}async head(){let e=this.create(new Request(this.request.url,{method:d,headers:this.request.headers}));return this.response(z,await e.fetch())}getAllowedMethods(){return[d,G,b]}};import{match as vt}from"path-to-regexp";var ne=class{routes=[];add(e){for(let[r,s,o]of e){let i=vt(s);this.routes.push({method:r,matcher:i,handler:o})}}match(e,r){let s=new URL(r).pathname;for(let o of this){if(o.method!==e)continue;let i=o.matcher(s);if(i)return{route:o,params:i.params}}return null}*[Symbol.iterator](){yield*this.routes}};var ht=class t extends oe{_routes=new ne;route(e,r,s){return this.routes([[e,r,s]]),this}routes(e){return this._routes.add(e),this}async dispatch(){let e=this._routes.match(this.request.method,this.request.url);if(!e)return super.dispatch();let{handler:r}=e.route;return t.isWorkerClass(r)?new r(this.request,this.env,this.ctx).fetch():r.call(this,e.params)}static isWorkerClass(e){return I.prototype.isPrototypeOf(e.prototype)}async get(){return this.response(O)}async put(){return this.response(O)}async post(){return this.response(O)}async patch(){return this.response(O)}async delete(){return this.response(O)}};export{k as BadRequest,oe as BasicWorker,x as CacheControl,M as ClonedResponse,or as DELETE,Be as Forbidden,d as GET,G as HEAD,z as Head,Pe as HtmlResponse,m as HttpError,$ as InternalServerError,F as JsonResponse,H as Method,D as MethodNotAllowed,R as MethodNotImplemented,O as NotFound,Ce as NotImplemented,N as NotModified,b as OPTIONS,he as OctetStream,y as Options,sr as PATCH,rr as POST,tr as PUT,Y as PreconditionFailed,De as R2ObjectStream,ht as RouteWorker,Fe as ServiceUnavailable,h as StatusCodes,P as SuccessResponse,ke as TextResponse,Re as Time,Ke as Unauthorized,j as UpgradeRequired,lt as WebSocketSessions,He as WebSocketUpgrade,C as WorkerResponse,Is as cache,Xs as cors,Ye as sortSearchParams,gs as stripSearchParams,no as websocket};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants/index.ts","../src/constants/cache.ts","../src/constants/methods.ts","../src/constants/time.ts","../src/middleware/middleware.ts","../src/guards/basic.ts","../src/guards/cache.ts","../src/middleware/cache/utils.ts","../src/constants/headers.ts","../src/utils/compare.ts","../src/utils/headers.ts","../src/middleware/cache/handler.ts","../src/guards/methods.ts","../src/responses.ts","../src/constants/media.ts","../src/utils/media.ts","../src/guards/responses.ts","../src/middleware/cors/constants.ts","../src/middleware/cors/utils.ts","../src/guards/cors.ts","../src/middleware/cors/handler.ts","../src/errors.ts","../src/constants/websocket.ts","../src/middleware/websocket/utils.ts","../src/middleware/websocket/handler.ts","../src/guards/websocket.ts","../src/websocket/events.ts","../src/websocket/base.ts","../src/websocket/new.ts","../src/websocket/restore.ts","../src/websocket/sessions.ts","../src/workers/base.ts","../src/guards/middleware.ts","../src/workers/middleware.ts","../src/workers/basic.ts","../src/routes.ts","../src/workers/route.ts"],"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 * https://github.com/prettymuchbryce/http-status-codes\n */\nexport { StatusCodes } from \"http-status-codes\";\n\nexport * from \"./cache\";\nexport * from \"./methods\";\nexport * from \"./time\";\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 * 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\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\nimport { Worker } from \"../interfaces/worker\";\n\n/**\n * Abstract base class for middleware.\n *\n * Middleware classes implement request/response processing logic in a\n * chainable manner. Each middleware receives a `Worker` object and a\n * `next` function that invokes the next middleware in the chain.\n *\n * Subclasses **must implement** the `handle` method.\n *\n * Example subclass:\n * ```ts\n * class LoggingMiddleware extends Middleware {\n * public async handle(worker: Worker, next: () => Promise<Response>): Promise<Response> {\n * console.log(`Processing request: ${worker.request.url}`);\n * const response = await next();\n * console.log(`Response status: ${response.status}`);\n * return response;\n * }\n * }\n * ```\n */\nexport abstract class Middleware {\n /**\n * Process a request in the middleware chain.\n *\n * @param worker - The `Worker` instance representing the request context.\n * @param next - Function to invoke the next middleware in the chain.\n * Must be called to continue the chain unless the middleware\n * terminates early (e.g., returns a response directly).\n * @returns A `Response` object, either returned directly or from `next()`.\n */\n public abstract handle(worker: Worker, next: () => Promise<Response>): Promise<Response>;\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 * 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 * Checks if a value is a function.\n *\n * @param value - The value to check.\n * @returns `true` if the value is a function, otherwise `false`.\n */\nexport function isFunction(value: unknown): value is Function {\n return typeof value === \"function\";\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 { isFunction, isString } from \"./basic\";\n\n/**\n * Asserts that a value is a string suitable for a cache name.\n *\n * If the value is `undefined`, this function does nothing.\n * Otherwise, it throws a `TypeError` if the value is not a string.\n *\n * @param value - The value to check.\n * @throws TypeError If the value is defined but not a string.\n */\nexport function assertCacheName(value: unknown): asserts value is string | undefined {\n if (value === undefined) return;\n if (!isString(value)) {\n throw new TypeError(\"Cache name must be a string.\");\n }\n}\n\n/**\n * Asserts that a value is a function suitable for `getKey`.\n *\n * If the value is `undefined`, this function does nothing.\n * Otherwise, it throws a `TypeError` if the value is not a function.\n *\n * @param value - The value to check.\n * @throws TypeError If the value is defined but not a function.\n */\nexport function assertGetKey(value: unknown): asserts value is Function | undefined {\n if (value === undefined) return;\n if (!isFunction(value)) {\n throw new TypeError(\"getKey must be a function.\");\n }\n}\n\n/**\n * Asserts that a value is a `URL` instance suitable for use as a cache key.\n *\n * If the value is not a `URL`, this function throws a `TypeError`.\n *\n * @param value - The value to check.\n * @throws TypeError If the value is not a `URL`.\n */\nexport function assertKey(value: unknown): asserts value is URL {\n if (!(value instanceof URL)) {\n throw new TypeError(\"getKey must return a URL.\");\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 *\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\";\nimport { HttpHeader } from \"../../constants/headers\";\nimport { lexCompare } from \"../../utils/compare\";\nimport { getHeaderValues } from \"../../utils/headers\";\nimport { VARY_WILDCARD } from \"./constants\";\n\n/** Base URL used for constructing cache keys. Only used internally. */\nconst VARY_CACHE_URL = \"https://vary\";\n\n/**\n * Determines whether a Response is cacheable.\n * - Status must be 200 OK\n * - Must not contain a Vary header with a wildcard (`*`)\n *\n * @param response The Response object to check.\n * @returns `true` if the response can be cached; `false` otherwise.\n */\nexport function isCacheable(response: Response): boolean {\n if (response.status !== StatusCodes.OK) return false;\n if (getVaryHeader(response).includes(VARY_WILDCARD)) return false;\n\n return true;\n}\n\n/**\n * Extracts and normalizes the `Vary` header from a Response.\n * - Splits comma-separated values\n * - Deduplicates\n * - Converts all values to lowercase\n * - Sorts lexicographically\n *\n * @param response The Response object containing headers.\n * @returns An array of normalized header names from the Vary header.\n */\nexport function getVaryHeader(response: Response): string[] {\n const values = getHeaderValues(response.headers, HttpHeader.VARY);\n return Array.from(new Set(values.map((v) => v.toLowerCase()))).sort(lexCompare);\n}\n\n/**\n * Filters out headers that should be ignored for caching, currently:\n * - `Accept-Encoding` (handled automatically by the platform)\n *\n * @param vary Array of normalized Vary header names.\n * @returns Array of headers used for computing cache variations.\n */\nexport function filterVaryHeader(vary: string[]): string[] {\n return vary\n .map((h) => h.toLowerCase())\n .filter((value) => value !== HttpHeader.ACCEPT_ENCODING.toLowerCase());\n}\n\n/**\n * Generates a Vary-aware cache key for a request.\n *\n * The key is based on:\n * 1. The provided `key` URL, which is normalized by default but can be fully customized\n * by the caller. For example, users can:\n * - Sort query parameters\n * - Remove the search/query string entirely\n * - Exclude certain query parameters\n * This allows full control over how this cache key is generated.\n * 2. The request headers listed in `vary` (after filtering and lowercasing).\n *\n * Behavior:\n * - Headers in `vary` are sorted and included in the key.\n * - The combination of the key URL and header values is base64-encoded to produce\n * a safe cache key.\n * - The resulting string is returned as an absolute URL rooted at `VARY_CACHE_URL`.\n *\n * @param request The Request object to generate a key for.\n * @param vary Array of header names from the `Vary` header that affect caching.\n * @param key The cache key to be used for this request. Can be modified by the caller for\n * custom cache key behavior.\n * @returns A string URL representing a unique cache key for this request + Vary headers.\n */\nexport function getVaryKey(request: Request, vary: string[], key: URL): string {\n const varyPairs: [string, string][] = [];\n const filtered = filterVaryHeader(vary);\n\n filtered.sort(lexCompare);\n for (const header of filtered) {\n const value = request.headers.get(header);\n if (value !== null) {\n varyPairs.push([header, value]);\n }\n }\n\n const encoded = base64UrlEncode(JSON.stringify([key.toString(), varyPairs]));\n return new URL(encoded, VARY_CACHE_URL).href;\n}\n\n/**\n * Encodes a string as URL-safe Base64.\n * - Converts to UTF-8 bytes\n * - Base64-encodes\n * - Replaces `+` with `-` and `/` with `_`\n * - Removes trailing `=`\n *\n * @param str The input string to encode.\n * @returns URL-safe Base64 string.\n */\nexport function base64UrlEncode(str: string): string {\n const utf8 = new TextEncoder().encode(str);\n let binary = \"\";\n for (const byte of utf8) {\n binary += String.fromCodePoint(byte);\n }\n return btoa(binary)\n .replaceAll(\"+\", \"-\")\n .replaceAll(\"/\", \"_\")\n .replace(/={1,2}$/, \"\");\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 * Internally used headers.\n */\nexport namespace HttpHeader {\n export const ACCEPT_ENCODING = \"Accept-Encoding\";\n export const ACCEPT_RANGES = \"Accept-Ranges\";\n export const ALLOW = \"Allow\";\n export const CACHE_CONTROL = \"Cache-Control\";\n export const CONNECTION = \"Connection\";\n export const CONTENT_DISPOSITION = \"Content-Disposition\";\n export const CONTENT_ENCODING = \"Content-Encoding\";\n export const CONTENT_LANGUAGE = \"Content-Language\";\n export const CONTENT_LENGTH = \"Content-Length\";\n export const CONTENT_RANGE = \"Content-Range\";\n export const CONTENT_TYPE = \"Content-Type\";\n export const CONTENT_MD5 = \"Content-MD5\";\n export const ETAG = \"ETag\";\n export const ORIGIN = \"Origin\";\n export const VARY = \"Vary\";\n\n // Cors Headers\n export const ACCESS_CONTROL_ALLOW_CREDENTIALS = \"Access-Control-Allow-Credentials\";\n export const ACCESS_CONTROL_ALLOW_HEADERS = \"Access-Control-Allow-Headers\";\n export const ACCESS_CONTROL_ALLOW_METHODS = \"Access-Control-Allow-Methods\";\n export const ACCESS_CONTROL_ALLOW_ORIGIN = \"Access-Control-Allow-Origin\";\n export const ACCESS_CONTROL_EXPOSE_HEADERS = \"Access-Control-Expose-Headers\";\n export const ACCESS_CONTROL_MAX_AGE = \"Access-Control-Max-Age\";\n\n // Websocket Headers\n export const SEC_WEBSOCKET_VERSION = \"Sec-WebSocket-Version\";\n export const UPGRADE = \"Upgrade\";\n}\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\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 * 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 { Middleware } from \"../middleware\";\nimport { Worker } from \"../../interfaces/worker\";\nimport { GET } from \"../../constants/methods\";\nimport { assertCacheName, assertGetKey, assertKey } from \"../../guards/cache\";\nimport { filterVaryHeader, getVaryHeader, getVaryKey, isCacheable } from \"./utils\";\nimport { lexCompare } from \"../../utils/compare\";\n\n/**\n * Creates a Vary-aware caching middleware for Workers.\n *\n * This middleware:\n * - Caches `GET` requests **only**.\n * - Respects the `Vary` header of responses, ensuring that requests\n * with different headers (e.g., `Origin`) receive the correct cached response.\n * - Skips caching for non-cacheable responses (e.g., error responses or\n * responses with `Vary: *`).\n *\n * @param cacheName Optional name of the cache to use. If omitted, the default cache is used.\n * @param getKey Optional function to compute a custom cache key from a request.\n * If omitted, the request URL is normalized and used as the key.\n * @returns A `Middleware` instance that can be used in a Worker pipeline.\n */\nexport function cache(cacheName?: string, getKey?: (request: Request) => URL): Middleware {\n assertCacheName(cacheName);\n assertGetKey(getKey);\n\n return new CacheHandler(cacheName, getKey);\n}\n\n/**\n * Returns a new URL with its query parameters sorted into a stable order.\n *\n * This is used for cache key generation: URLs that differ only in the\n * order of their query parameters will normalize to the same key.\n *\n * @param request - The incoming Request whose URL will be normalized.\n * @returns A new URL with query parameters sorted by name.\n */\nexport function sortSearchParams(request: Request): URL {\n const url = new URL(request.url);\n const sorted = new URLSearchParams(\n [...url.searchParams.entries()].sort(([a], [b]) => lexCompare(a, b)),\n );\n url.search = sorted.toString();\n url.hash = \"\";\n return url;\n}\n\n/**\n * Returns a new URL with all query parameters removed.\n *\n * This is used when query parameters are not relevant to cache lookups,\n * ensuring that variants of the same resource share a single cache entry.\n *\n * @param request - The incoming Request whose URL will be normalized.\n * @returns A new URL with no query parameters.\n */\nexport function stripSearchParams(request: Request): URL {\n const url = new URL(request.url);\n url.search = \"\";\n url.hash = \"\";\n return url;\n}\n\n/**\n * Cache Middleware Implementation\n * @see {@link cache}\n */\nclass CacheHandler extends Middleware {\n constructor(\n private readonly cacheName?: string,\n private readonly getKey?: (request: Request) => URL,\n ) {\n super();\n this.cacheName = cacheName?.trim() || undefined;\n }\n\n /**\n * Handles an incoming request.\n * - Bypasses caching for non-`GET` requests.\n * - Checks the cache for a stored response.\n * - Calls next if no cached response exists.\n * - Caches the response if it is cacheable.\n *\n * @param worker The Worker instance containing the request and context.\n * @param next Function to call the next middleware or origin fetch.\n * @returns A cached or freshly fetched Response.\n */\n public override async handle(worker: Worker, next: () => Promise<Response>): Promise<Response> {\n if (worker.request.method !== GET) {\n return next();\n }\n\n const cache = this.cacheName ? await caches.open(this.cacheName) : caches.default;\n const cached = await this.getCached(cache, worker.request);\n if (cached) return cached;\n\n const response = await next();\n\n this.setCached(cache, worker, response);\n return response;\n }\n\n /**\n * Retrieves a cached response for a given request.\n * - Checks both the base cache key and any Vary-specific keys.\n *\n * @param cache The Cache object to check.\n * @param request The request to retrieve a cached response for.\n * @returns A cached Response if available, otherwise `undefined`.\n */\n public async getCached(cache: Cache, request: Request): Promise<Response | undefined> {\n const url = this.getCacheKey(request);\n const response = await cache.match(url);\n if (!response) return;\n\n const vary = this.getFilteredVary(response);\n if (vary.length === 0) return response;\n\n const key = getVaryKey(request, vary, url);\n return cache.match(key);\n }\n\n /**\n * Caches a response if it is cacheable.\n *\n * Behavior:\n * - Always stores the response under the main cache key. This ensures that\n * the response’s Vary headers are available for later cache lookups.\n * - If the response varies based on certain request headers (per the Vary header),\n * also stores a copy under a Vary-specific cache key so future requests\n * with matching headers can retrieve the correct response.\n *\n * @param cache The Cache object to store the response in.\n * @param worker The Worker instance containing the request and context.\n * @param response The Response to cache.\n */\n public async setCached(cache: Cache, worker: Worker, response: Response): Promise<void> {\n if (!isCacheable(response)) return;\n\n const url = this.getCacheKey(worker.request);\n\n // Always store the main cache entry to preserve Vary headers\n worker.ctx.waitUntil(cache.put(url, response.clone()));\n\n // Store request-specific cache entry if the response varies\n const vary = this.getFilteredVary(response);\n if (vary.length > 0) {\n worker.ctx.waitUntil(\n cache.put(getVaryKey(worker.request, vary, url), response.clone()),\n );\n }\n }\n\n /**\n * Extracts and filters the `Vary` header from a response.\n *\n * @param response - The HTTP response to inspect.\n * @returns An array of filtered header names from the `Vary` header.\n */\n public getFilteredVary(response: Response): string[] {\n return filterVaryHeader(getVaryHeader(response));\n }\n\n /**\n * Returns the cache key for a request.\n *\n * By default, this is a normalized URL including the path and query string.\n * However, users can provide a custom `getKey` function when creating the\n * `cache` middleware to fully control how the cache keys are generated.\n *\n * For example, a custom function could:\n * - Sort or remove query parameters\n * - Exclude the search/query string entirely\n * - Modify the path or host\n *\n * This allows complete flexibility over cache key generation.\n *\n * @param request The Request object to generate a cache key for.\n * @returns A URL representing the main cache key for this request.\n */\n public getCacheKey(request: Request): URL {\n const key = this.getKey ? this.getKey(request) : sortSearchParams(request);\n assertKey(key);\n\n key.hash = \"\";\n return key;\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 { Method } from \"../constants/methods\";\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 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\nimport { getReasonPhrase, StatusCodes } from \"http-status-codes\";\nimport { CacheControl } from \"./constants/cache\";\nimport { setHeader, mergeHeader, filterHeaders } from \"./utils/headers\";\nimport { UTF8_CHARSET, MediaType } from \"./constants/media\";\nimport { FORBIDDEN_204_HEADERS, FORBIDDEN_304_HEADERS, HttpHeader } from \"./constants/headers\";\nimport { OctetStreamInit } from \"./interfaces/response\";\nimport { withCharset } from \"./utils/media\";\nimport { assertOctetStreamInit } from \"./guards/responses\";\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 /** Enable websocket responses. */\n public webSocket: WebSocket | null = 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 * Wraps an existing Response and clones its body, headers, and status.\n */\nexport class ClonedResponse extends WorkerResponse {\n constructor(response: Response, cache?: CacheControl) {\n const clone = response.clone();\n super(clone.body, cache);\n this.headers = new Headers(clone.headers);\n this.status = clone.status;\n this.statusText = clone.statusText;\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 * @param source - The R2 object to stream.\n * @param cache - Optional caching information.\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(null);\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` preflight requests.\n */\nexport class Options extends WorkerResponse {\n constructor() {\n super();\n this.status = StatusCodes.NO_CONTENT;\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 * 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 { OctetStreamInit } from \"../interfaces\";\nimport { isNumber } from \"./basic\";\n\n/**\n * Asserts that a given value is a valid `OctetStreamInit` object.\n *\n * Properties:\n * - `size` (required): must be a non-negative number.\n * - `offset` (optional): if provided, must be a number between 0 and `size`.\n * - `length` (optional): if provided, must be a non-negative number such that `offset + length <= size`.\n *\n * If `offset` or `length` are `undefined`, they are considered as `0` and `size` respectively.\n *\n * Throws an error if validation fails.\n *\n * Acts as a TypeScript type predicate, so after calling it, `value` is narrowed to `OctetStreamInit`.\n *\n * @param value - The value to validate as `OctetStreamInit`.\n * @throws {TypeError} If the value is not a non-null object.\n * @throws {RangeError} If `size`, `offset`, or `length` are invalid.\n * @returns `true` if the value is a valid `OctetStreamInit`.\n */\nexport function assertOctetStreamInit(value: unknown): asserts value is OctetStreamInit {\n if (typeof value !== \"object\" || value === null) {\n throw new TypeError(\"OctetStreamInit must be an object.\");\n }\n\n const obj = value as Record<string, unknown>;\n\n // size\n const size = obj[\"size\"];\n if (!isNumber(size) || size < 0 || !Number.isInteger(size)) {\n throw new RangeError(\n `OctetStreamInit.size must be a non-negative integer (size=${JSON.stringify(size)}).`,\n );\n }\n\n // offset\n const offset = obj[\"offset\"] ?? 0;\n if (!isNumber(offset) || offset < 0 || offset > size || !Number.isInteger(offset)) {\n throw new RangeError(\n `OctetStreamInit.offset must be a non-negative integer less than or equal to size (size=${JSON.stringify(size)}, offset=${JSON.stringify(offset)}).`,\n );\n }\n\n // length\n const length = obj[\"length\"] ?? size - offset;\n if (!isNumber(length) || length < 0 || offset + length > size || !Number.isInteger(length)) {\n throw new RangeError(\n `OctetStreamInit.length must be a non-negative integer less than or equal to size - offset (size=${JSON.stringify(size)}, offset=${JSON.stringify(offset)}, length=${JSON.stringify(length)}).`,\n );\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 { StatusCodes } from \"../../constants\";\nimport { HttpHeader } from \"../../constants/headers\";\nimport { GET, HEAD, Method, OPTIONS } from \"../../constants/methods\";\nimport { Time } from \"../../constants/time\";\nimport { CorsConfig } from \"../../interfaces/cors\";\n\nexport const ALLOW_ALL_ORIGINS = \"*\";\n\n/**\n * Set of HTTP methods considered \"simple\" under the `CORS` specification.\n *\n * Simple methods do not trigger a preflight request on their own.\n * (Other factors like headers can still cause a preflight.)\n */\nexport const SIMPLE_METHODS = new Set<Method>([GET, HEAD, OPTIONS]);\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 { HttpHeader } from \"../../constants/headers\";\nimport { assertMethods } from \"../../guards/methods\";\nimport { CorsConfig } from \"../../interfaces/cors\";\nimport { Worker } from \"../../interfaces/worker\";\nimport { ClonedResponse, Options } from \"../../responses\";\nimport { mergeHeader, setHeader } from \"../../utils/headers\";\nimport { ALLOW_ALL_ORIGINS, SIMPLE_METHODS, SKIP_CORS_STATUSES } from \"./constants\";\n\n/**\n * Handles a `CORS` preflight `OPTIONS` request.\n *\n * Sets the appropriate`CORS`headers based on the provided configuration\n * and the origin of the request.\n *\n * @param worker - The Worker handling the request.\n * @param cors - The `CORS` configuration.\n * @returns A Response object for the preflight request.\n */\nexport async function options(worker: Worker, cors: CorsConfig): Promise<Response> {\n const options = new Options();\n const origin = getOrigin(worker.request);\n\n if (origin) {\n setAllowOrigin(options.headers, cors, origin);\n setAllowCredentials(options.headers, cors, origin);\n }\n\n setAllowMethods(options.headers, worker);\n setAllowHeaders(options.headers, cors);\n setMaxAge(options.headers, cors);\n\n return options.response();\n}\n\n/**\n * Applies`CORS`headers to an existing response.\n *\n * Useful for normal (non-preflight) responses where the response\n * should include`CORS`headers based on the request origin.\n *\n * @param response - The original Response object.\n * @param worker - The Worker handling the request.\n * @param cors - The`CORS`configuration.\n * @returns A new Response object with`CORS`headers applied.\n */\nexport async function apply(\n response: Response,\n worker: Worker,\n cors: CorsConfig,\n): Promise<Response> {\n const clone = new ClonedResponse(response);\n const origin = getOrigin(worker.request);\n\n deleteCorsHeaders(clone.headers);\n\n if (origin) {\n setAllowOrigin(clone.headers, cors, origin);\n setAllowCredentials(clone.headers, cors, origin);\n setExposedHeaders(clone.headers, cors);\n }\n\n return clone.response();\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 } else {\n if (cors.allowedOrigins.includes(origin)) {\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_ORIGIN, origin);\n }\n mergeHeader(headers, HttpHeader.VARY, HttpHeader.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 * but only for non-simple methods.\n *\n * Simple methods (`GET`, `HEAD`, `OPTIONS`) are automatically allowed by the\n *`CORS`spec, so this function only adds methods beyond those.\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 methods = worker.getAllowedMethods();\n assertMethods(methods);\n\n const allowed = methods.filter((method) => !SIMPLE_METHODS.has(method));\n\n if (allowed.length > 0) {\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_METHODS, allowed);\n }\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 if (cors.allowedHeaders.length > 0) {\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_HEADERS, cors.allowedHeaders);\n }\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 * @param response - The Response object to inspect\n * @returns `true` if`CORS`should be skipped, `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 { CorsInit } from \"../interfaces/cors\";\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 */\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\nimport { apply, options, skipCors } from \"./utils\";\nimport { Worker } from \"../../interfaces/worker\";\nimport { Middleware } from \"../middleware\";\nimport { CorsConfig, CorsInit } from \"../../interfaces/cors\";\nimport { defaultCorsConfig } from \"./constants\";\nimport { OPTIONS } from \"../../constants/methods\";\nimport { assertCorsInit } from \"../../guards/cors\";\n\n/**\n * Creates 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\n/**\n * Cors Middleware Implementation\n * @see {@link cors}\n */\nclass CorsHandler extends Middleware {\n /** The configuration used for this instance, with all defaults applied. */\n private readonly config: CorsConfig;\n\n /**\n * Create a new`CORS`middleware instance.\n *\n * @param init - Partial configuration to override the defaults. Any values\n * not provided will use `defaultCorsConfig`.\n */\n constructor(init?: CorsInit) {\n super();\n this.config = { ...defaultCorsConfig, ...init };\n }\n\n /**\n * Applies`CORS`headers to a request.\n *\n * - Returns a preflight response for `OPTIONS` requests.\n * - For other methods, calls `next()` and applies`CORS`headers to the result.\n *\n * @param worker - The Worker handling the request.\n * @param next - Function to invoke the next middleware.\n * @returns Response with`CORS`headers applied.\n */\n public override async handle(worker: Worker, next: () => Promise<Response>): Promise<Response> {\n if (worker.request.method === OPTIONS) {\n return options(worker, this.config);\n }\n\n const response = await next();\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 { getReasonPhrase, StatusCodes } from \"http-status-codes\";\nimport { JsonResponse } from \"./responses\";\nimport { ErrorJson } from \"./interfaces/error\";\nimport { Worker } from \"./interfaces/worker\";\nimport { CacheControl } from \"./constants/cache\";\nimport { HttpHeader } from \"./constants/headers\";\nimport { assertMethods } from \"./guards/methods\";\nimport { WS_VERSION } from \"./constants/websocket\";\n\n/**\n * Generic HTTP error response.\n * Sends a JSON body with status, error message, and details.\n */\nexport class HttpError extends JsonResponse {\n /**\n * @param worker The worker handling the request.\n * @param status HTTP status code.\n * @param details Optional detailed error message.\n */\n constructor(\n status: StatusCodes,\n protected readonly details?: string,\n ) {\n const json: ErrorJson = {\n status,\n error: getReasonPhrase(status),\n details: details ?? \"\",\n };\n super(json, CacheControl.DISABLE, status);\n }\n}\n\n/** 400 Bad Request error response. */\nexport class BadRequest extends HttpError {\n constructor(details?: string) {\n super(StatusCodes.BAD_REQUEST, details);\n }\n}\n\n/** 401 Unauthorized error response. */\nexport class Unauthorized extends HttpError {\n constructor(details?: string) {\n super(StatusCodes.UNAUTHORIZED, details);\n }\n}\n\n/** 403 Forbidden error response. */\nexport class Forbidden extends HttpError {\n constructor(details?: string) {\n super(StatusCodes.FORBIDDEN, details);\n }\n}\n\n/** 404 Not Found error response. */\nexport class NotFound extends HttpError {\n constructor(details?: string) {\n super(StatusCodes.NOT_FOUND, details);\n }\n}\n\n/** 405 Method Not Allowed error response. */\nexport class MethodNotAllowed extends HttpError {\n constructor(worker: Worker) {\n const methods = worker.getAllowedMethods();\n assertMethods(methods);\n\n super(StatusCodes.METHOD_NOT_ALLOWED, `${worker.request.method} method not allowed.`);\n this.setHeader(HttpHeader.ALLOW, methods);\n }\n}\n\n/** 426 Upgrade Required error response. */\nexport class UpgradeRequired extends HttpError {\n constructor() {\n super(StatusCodes.UPGRADE_REQUIRED);\n this.headers.set(HttpHeader.SEC_WEBSOCKET_VERSION, WS_VERSION);\n }\n}\n\n/** 500 Internal Server Error response. */\nexport class InternalServerError extends HttpError {\n constructor(details?: string) {\n super(StatusCodes.INTERNAL_SERVER_ERROR, details);\n }\n}\n\n/** 501 Not Implemented error response. */\nexport class NotImplemented extends HttpError {\n constructor(details?: string) {\n super(StatusCodes.NOT_IMPLEMENTED, details);\n }\n}\n\n/** 501 Method Not Implemented error response for unsupported HTTP methods. */\nexport class MethodNotImplemented extends NotImplemented {\n constructor(worker: Worker) {\n super(`${worker.request.method} method not implemented.`);\n }\n}\n\n/** 503 Service Unavailable error response. */\nexport class ServiceUnavailable extends HttpError {\n constructor(details?: string) {\n super(StatusCodes.SERVICE_UNAVAILABLE, details);\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/** WebSocket upgrade header value */\nexport const WS_UPGRADE = \"upgrade\";\n/** WebSocket protocol header value */\nexport const WS_WEBSOCKET = \"websocket\";\n/** WebSocket protocol version */\nexport const WS_VERSION = \"13\";\n/** Max close code a user can send */\nexport const WS_MAX_CLOSE_CODE = 4999;\n/** Max number of reason chars a user can send */\nexport const WS_MAX_REASON_CHARS = 123;\n\n/** WebSocket close codes */\nexport const CloseCode = {\n NORMAL: 1000,\n GOING_AWAY: 1001,\n PROTOCOL_ERROR: 1002,\n UNSUPPORTED_DATA: 1003,\n NO_STATUS: 1005,\n ABNORMAL: 1006,\n INVALID_PAYLOAD: 1007,\n POLICY_VIOLATION: 1008,\n MESSAGE_TOO_BIG: 1009,\n MISSING_EXTENSION: 1010,\n INTERNAL_ERROR: 1011,\n TLS_HANDSHAKE: 1015,\n} as const;\n\n/** WebSocket RESERVED close codes */\nexport const WS_RESERVED_CODES = new Set<number>([\n CloseCode.NO_STATUS,\n CloseCode.ABNORMAL,\n CloseCode.TLS_HANDSHAKE,\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 { WS_UPGRADE, WS_VERSION, WS_WEBSOCKET } from \"../../constants/websocket\";\nimport { getHeaderValues } from \"../../utils/headers\";\n\nexport function hasConnectionHeader(headers: Headers): boolean {\n return getHeaderValues(headers, HttpHeader.CONNECTION).some(\n (value) => value.toLowerCase() === WS_UPGRADE,\n );\n}\n\nexport function hasUpgradeHeader(headers: Headers): boolean {\n return getHeaderValues(headers, HttpHeader.UPGRADE).some(\n (value) => value.toLowerCase() === WS_WEBSOCKET,\n );\n}\n\nexport function hasWebSocketVersion(headers: Headers): boolean {\n return headers.get(HttpHeader.SEC_WEBSOCKET_VERSION)?.trim() === WS_VERSION;\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 { GET } from \"../../constants/methods\";\nimport { BadRequest, UpgradeRequired } from \"../../errors\";\nimport { Worker } from \"../../interfaces/worker\";\nimport { Middleware } from \"../middleware\";\nimport { hasConnectionHeader, hasUpgradeHeader, hasWebSocketVersion } from \"./utils\";\n\nexport function websocket(path: string = \"/\"): Middleware {\n return new WebSocketHandler(path);\n}\n\nclass WebSocketHandler extends Middleware {\n constructor(private readonly path: string) {\n super();\n }\n\n public handle(worker: Worker, next: () => Promise<Response>): Promise<Response> {\n if (worker.request.method !== GET) {\n return next();\n }\n\n if (this.getPath(worker.request) !== this.path) {\n return next();\n }\n\n const headers = worker.request.headers;\n if (!hasConnectionHeader(headers)) {\n return new BadRequest(\"Missing or invalid Connection header\").response();\n }\n if (!hasUpgradeHeader(headers)) {\n return new BadRequest(\"Missing or invalid Upgrade header\").response();\n }\n if (!hasWebSocketVersion(headers)) {\n return new UpgradeRequired().response();\n }\n\n return next();\n }\n\n private getPath(request: Request): string {\n return new URL(request.url).pathname;\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 { CloseCode, WS_MAX_CLOSE_CODE, WS_MAX_REASON_CHARS, WS_RESERVED_CODES } from \"../constants/websocket\";\nimport { isNumber, isString } from \"./basic\";\n\nexport function isBinary(value: unknown): value is ArrayBuffer | ArrayBufferView {\n return value instanceof ArrayBuffer || ArrayBuffer.isView(value);\n}\n\nexport function isSendable(value: unknown): value is string | ArrayBuffer | ArrayBufferView {\n if (isString(value)) return value.length > 0;\n if (isBinary(value)) return value.byteLength > 0;\n return false;\n}\n\nexport function safeCloseCode(code?: number): number {\n if (!isNumber(code)) return CloseCode.NORMAL;\n if (isCodeInRange(code) && !isReservedCode(code)) return code;\n return CloseCode.NORMAL;\n}\n\nexport function isCodeInRange(code: number): boolean {\n return code >= CloseCode.NORMAL && code <= WS_MAX_CLOSE_CODE;\n}\n\nexport function isReservedCode(code: number): boolean {\n return WS_RESERVED_CODES.has(code);\n}\n\nexport function safeReason(reason?: string): string | undefined {\n if (!isString(reason)) return;\n return reason.replaceAll(/[^\\x20-\\x7E]/g, \"\").slice(0, WS_MAX_REASON_CHARS);\n}\n\nexport function assertSerializable(value: unknown): asserts value is object {\n if (value === null || typeof value !== \"object\") {\n throw new TypeError(\"WebSocket attachment must be an object\");\n }\n try {\n JSON.stringify(value);\n } catch {\n throw new TypeError(\"WebSocket attachment is not serializable\");\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 {\n CustomEventType,\n EventOptions,\n ExtendedEventListener,\n ExtendedEventMap,\n ExtendedEventType,\n} from \"../interfaces/websocket\";\n\nexport abstract class WebSocketEvents {\n protected readonly server: WebSocket;\n\n private static isCustomEvent(type: ExtendedEventType): boolean {\n return [\"open\", \"warn\"].includes(type);\n }\n\n private customListeners: {\n [K in ExtendedEventType]?: ((ev: ExtendedEventMap[K]) => void)[];\n } = {};\n\n constructor(server: WebSocket) {\n this.server = server;\n }\n\n public addEventListener<K extends ExtendedEventType>(\n type: K,\n listener: ExtendedEventListener<K>,\n options?: EventOptions,\n ): void {\n if (WebSocketEvents.isCustomEvent(type)) {\n let arr = this.customListeners[type];\n if (!arr) {\n arr = [];\n this.customListeners[type] = arr;\n }\n arr.push(listener);\n } else {\n const finalOptions = type === \"close\" ? { ...options, once: true } : options;\n this.server.addEventListener(\n type as keyof WebSocketEventMap,\n listener as EventListener,\n finalOptions,\n );\n }\n }\n\n public removeEventListener<K extends ExtendedEventType>(\n type: K,\n listener: ExtendedEventListener<K>,\n ): void {\n if (WebSocketEvents.isCustomEvent(type)) {\n const arr = this.customListeners[type];\n if (arr) {\n const index = arr.indexOf(listener);\n if (index !== -1) arr.splice(index, 1);\n }\n } else {\n this.server.removeEventListener(\n type as keyof WebSocketEventMap,\n listener as EventListener,\n );\n }\n }\n\n private dispatch<K extends CustomEventType>(\n type: K,\n ev: ExtendedEventMap[K],\n once: boolean = false,\n ): void {\n const listeners = this.customListeners[type]?.slice() ?? [];\n if (once) {\n this.customListeners[type] = [];\n }\n for (const listener of listeners) {\n listener(ev);\n }\n }\n\n protected warn(msg: string) {\n this.dispatch(\"warn\", { type: \"warn\", message: msg });\n }\n\n protected open() {\n this.dispatch(\"open\", new Event(\"open\"), true);\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 { assertSerializable, isSendable, safeCloseCode, safeReason } from \"../guards/websocket\";\nimport { WSAttachment } from \"../interfaces/websocket\";\nimport { WebSocketEvents } from \"./events\";\n\nexport abstract class BaseWebSocket<A extends WSAttachment> extends WebSocketEvents {\n protected accepted = false;\n protected readonly server: WebSocket;\n\n constructor(server: WebSocket) {\n super(server);\n this.server = server;\n this.server.addEventListener(\"close\", this.onclose);\n }\n\n public send(data: string | ArrayBuffer | ArrayBufferView): void {\n if (this.isState(WebSocket.CONNECTING, WebSocket.CLOSED)) {\n this.warn(\"Cannot send: WebSocket not open\");\n return;\n }\n if (!isSendable(data)) {\n this.warn(\"Cannot send: empty or invalid data\");\n return;\n }\n\n this.server.send(data);\n }\n\n public get attachment(): A {\n return (this.server.deserializeAttachment() ?? {}) as A;\n }\n\n public attach(attachment?: Partial<A> | null): void {\n if (attachment === undefined) return;\n if (attachment === null) {\n this.server.serializeAttachment({});\n } else {\n const current = this.attachment;\n const merged = { ...current, ...attachment };\n assertSerializable(merged);\n this.server.serializeAttachment(merged);\n }\n }\n\n public get readyState(): number {\n if (!this.accepted) return WebSocket.CONNECTING;\n return this.server.readyState;\n }\n\n public isState(...states: number[]): boolean {\n return states.includes(this.readyState);\n }\n\n public close(code?: number, reason?: string): void {\n this.server.removeEventListener(\"close\", this.onclose);\n this.server.close(safeCloseCode(code), safeReason(reason));\n }\n\n private readonly onclose = (event: CloseEvent): void => {\n this.close(event.code, event.reason);\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 { WSAttachment, WebSocketConnection } from \"../interfaces/websocket\";\nimport { BaseWebSocket } from \"./base\";\n\nexport abstract class NewConnectionBase<A extends WSAttachment>\n extends BaseWebSocket<A>\n implements WebSocketConnection<A>\n{\n private readonly client: WebSocket;\n\n public constructor() {\n const pair = new WebSocketPair();\n const [client, server] = [pair[0], pair[1]];\n super(server);\n this.client = client;\n }\n\n public acceptWebSocket(ctx: DurableObjectState, tags?: string[]): WebSocket {\n ctx.acceptWebSocket(this.server, tags);\n return this.ready();\n }\n\n public accept(): WebSocket {\n this.server.accept();\n return this.ready();\n }\n\n private ready(): WebSocket {\n this.accepted = true;\n this.open();\n\n return this.client;\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 { WSAttachment, WebSocketConnection } from \"../interfaces/websocket\";\nimport { BaseWebSocket } from \"./base\";\n\nexport abstract class RestoredConnectionBase<A extends WSAttachment>\n extends BaseWebSocket<A>\n implements WebSocketConnection<A>\n{\n constructor(ws: WebSocket) {\n super(ws);\n this.accepted = true;\n }\n\n public accept(): WebSocket {\n throw new Error(\"Do not call accept() on restore\");\n }\n\n public acceptWebSocket(): WebSocket {\n throw new Error(\"Do not call acceptWebSocket() on restore\");\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 { WSAttachment, WebSocketConnection } from \"../interfaces/websocket\";\nimport { NewConnectionBase } from \"./new\";\nimport { RestoredConnectionBase } from \"./restore\";\n\nexport class WebSocketSessions<A extends WSAttachment = WSAttachment> {\n private readonly map = new Map<WebSocket, WebSocketConnection<A>>();\n\n public create(attachment?: Partial<A>): WebSocketConnection<A> {\n class NewConnection extends NewConnectionBase<A> {\n constructor(private readonly sessions: WebSocketSessions<A>) {\n super();\n }\n\n public override accept(): WebSocket {\n this.addEventListener(\"close\", () => this.sessions.unregister(this.server));\n this.sessions.register(this.server, this);\n return super.accept();\n }\n\n public override acceptWebSocket(ctx: DurableObjectState, tags?: string[]): WebSocket {\n this.sessions.register(this.server, this);\n return super.acceptWebSocket(ctx, tags);\n }\n }\n\n const connection = new NewConnection(this);\n connection.attach(attachment);\n return connection;\n }\n\n public restore(ws: WebSocket): WebSocketConnection<A> {\n class RestoredConnection extends RestoredConnectionBase<A> {\n constructor(sessions: WebSocketSessions<A>, restore: WebSocket) {\n super(restore);\n sessions.register(this.server, this);\n }\n }\n return new RestoredConnection(this, ws);\n }\n\n public restoreAll(all: WebSocket[]): ReadonlyArray<WebSocketConnection<A>> {\n const restored: WebSocketConnection<A>[] = [];\n for (const ws of all) {\n restored.push(this.restore(ws));\n }\n return restored;\n }\n\n public get(ws: WebSocket): WebSocketConnection<A> | undefined {\n return this.map.get(ws);\n }\n\n public values(): IterableIterator<WebSocketConnection<A>> {\n return this.map.values();\n }\n\n public keys(): IterableIterator<WebSocket> {\n return this.map.keys();\n }\n\n public close(ws: WebSocket, code?: number, reason?: string): boolean {\n const con = this.get(ws);\n if (con) con.close(code, reason);\n\n return this.unregister(ws);\n }\n\n public *[Symbol.iterator](): IterableIterator<WebSocketConnection<A>> {\n yield* this.values();\n }\n\n private register(ws: WebSocket, con: WebSocketConnection<A>): void {\n this.map.set(ws, con);\n }\n\n private unregister(ws: WebSocket): boolean {\n return this.map.delete(ws);\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 { Method } from \"../constants/methods\";\nimport { assertMethods, isMethod } from \"../guards/methods\";\nimport { FetchHandler } from \"../interfaces/fetch\";\nimport { Worker, WorkerClass } from \"../interfaces/worker\";\n\n/**\n * Provides the foundational structure for handling requests,\n * environment bindings, and the worker execution context.\n *\n * Features:\n * - Holds the current `Request` object (`request` getter).\n * - Provides access to environment bindings (`env` getter).\n * - Provides access to the worker execution context (`ctx` getter).\n * - Subclasses must implement `fetch()` to process the request.\n */\nexport abstract class BaseWorker implements Worker {\n constructor(\n private readonly _request: Request,\n private readonly _env: Env,\n private readonly _ctx: ExecutionContext,\n ) {}\n\n /** The Request object associated with this worker invocation */\n public get request(): Request {\n return this._request;\n }\n\n /** Environment bindings (e.g., KV, secrets, or other globals) */\n public get env(): Env {\n return this._env;\n }\n\n /** Execution context for background tasks or `waitUntil` */\n public get ctx(): ExecutionContext {\n return this._ctx;\n }\n\n /**\n * Dispatches the incoming request to the appropriate handler and produces a response.\n *\n * Subclasses must implement this method to define how the worker generates a `Response`\n * for the current request. This is the central point where request processing occurs.\n *\n * @returns A Promise that resolves to the `Response` for the request.\n */\n protected abstract dispatch(): Promise<Response>;\n\n /**\n * Checks if the given HTTP method is allowed for this worker.\n * @param method HTTP method string\n * @returns true if the method is allowed\n */\n public isAllowed(method: string): boolean {\n const methods = this.getAllowedMethods();\n assertMethods(methods);\n\n return isMethod(method) && methods.includes(method);\n }\n\n public abstract getAllowedMethods(): Method[];\n\n /**\n * Creates a new instance of the current Worker subclass.\n *\n * @param request - The {@link Request} to pass to the new worker instance.\n * @returns A new worker instance of the same subclass as `this`.\n */\n protected create(request: Request): this {\n const ctor = this.constructor as WorkerClass<this>;\n return new ctor(request, this.env, this.ctx);\n }\n\n /**\n * Process the {@link Request} and produce a {@link Response}.\n *\n * @returns A {@link Response} promise for the {@link Request}.\n */\n public abstract fetch(): Promise<Response>;\n\n /**\n * Simplify and standardize {@link Response} creation by extending {@link WorkerResponse}\n * or any of its subclasses and passing to this method.\n *\n * Or directly use any of the built-in classes.\n *\n * ```ts\n * this.response(TextResponse, \"Hello World!\")\n * ```\n *\n * @param ResponseClass The response class to instantiate\n * @param args Additional constructor arguments\n * @returns A Promise resolving to the {@link Response} object\n */\n protected async response<\n Ctor extends new (...args: any[]) => { response(): Promise<Response> },\n >(ResponseClass: Ctor, ...args: ConstructorParameters<Ctor>): Promise<Response> {\n return new ResponseClass(...args).response();\n }\n\n /**\n * **Ignite** your `Worker` implementation into a Cloudflare handler.\n *\n * @returns A `FetchHandler` that launches a new worker instance for each request.\n *\n * ```ts\n * export default MyWorker.ignite();\n * ```\n */\n public static ignite<W extends Worker>(this: WorkerClass<W>): FetchHandler {\n return {\n fetch: (request: Request, env: Env, ctx: ExecutionContext) =>\n new this(request, env, ctx).fetch(),\n };\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 { Middleware } from \"../middleware/middleware\";\n\n/**\n * Asserts at runtime that a value is a Middleware instance.\n *\n * @param handler - The value to check.\n * @throws TypeError If `handler` is not a `Middleware` subclass instance.\n */\nexport function assertMiddleware(handler: unknown): asserts handler is Middleware {\n if (handler instanceof Middleware) return;\n\n throw new TypeError(\"Handler must be a subclass of Middleware.\");\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 { BaseWorker } from \"./base\";\nimport { Middleware } from \"../middleware/middleware\";\nimport { assertMiddleware } from \"../guards/middleware\";\n\n/** Internal base worker for handling middleware chains. */\nexport abstract class MiddlewareWorker extends BaseWorker {\n /** Middleware handlers registered for this worker. */\n protected readonly middlewares: Middleware[] = [];\n\n /**\n * Hook for subclasses to perform any initialization.\n */\n protected init(): void | Promise<void> {\n return;\n }\n\n /**\n * Add a middleware instance to this worker.\n *\n * The middleware will run for every request handled by this worker,\n * in the order they are added.\n *\n * @param handler - The middleware to run.\n * @returns `this` to allow chaining multiple `.use()` calls.\n */\n public use(handler: Middleware): this {\n assertMiddleware(handler);\n\n this.middlewares.push(handler);\n return this;\n }\n\n /**\n * Executes the middleware chain and dispatches the request.\n *\n * @returns The Response produced by the last middleware or `dispatch()`.\n */\n public override async fetch(): Promise<Response> {\n const chain = this.middlewares.reduceRight(\n (next, handler) => () => handler.handle(this, next),\n () => this.dispatch(),\n );\n return await chain();\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 { MethodNotAllowed, InternalServerError, MethodNotImplemented } from \"../errors\";\nimport { MiddlewareWorker } from \"./middleware\";\nimport { Head, Options } from \"../responses\";\nimport { Method, GET, HEAD, OPTIONS } from \"../constants/methods\";\n\n/**\n * Basic worker class providing HTTP method dispatching and error handling.\n */\nexport abstract class BasicWorker extends MiddlewareWorker {\n /**\n * Entry point to handle a fetch request.\n */\n public override async fetch(): Promise<Response> {\n if (!this.isAllowed(this.request.method)) {\n return this.response(MethodNotAllowed, this);\n }\n\n try {\n await this.init();\n return await super.fetch();\n } catch (error) {\n console.error(error);\n return this.response(InternalServerError);\n }\n }\n\n /**\n * Dispatches the request to the method-specific handler.\n */\n protected override async dispatch(): Promise<Response> {\n const method = this.request.method as Method;\n const handler: Record<Method, () => Promise<Response>> = {\n GET: () => this.get(),\n PUT: () => this.put(),\n HEAD: () => this.head(),\n POST: () => this.post(),\n PATCH: () => this.patch(),\n DELETE: () => this.delete(),\n OPTIONS: () => this.options(),\n };\n\n return (handler[method] ?? (() => this.response(MethodNotAllowed, this)))();\n }\n\n /** Override and implement this method for `GET` requests. */\n protected async get(): Promise<Response> {\n return this.response(MethodNotImplemented, this);\n }\n\n /** Override and implement this method for `PUT` requests. */\n protected async put(): Promise<Response> {\n return this.response(MethodNotImplemented, this);\n }\n\n /** Override and implement this method for `POST` requests. */\n protected async post(): Promise<Response> {\n return this.response(MethodNotImplemented, this);\n }\n\n /** Override and implement this method for `PATCH` requests. */\n protected async patch(): Promise<Response> {\n return this.response(MethodNotImplemented, this);\n }\n\n /** Override and implement this method for `DELETE` requests. */\n protected async delete(): Promise<Response> {\n return this.response(MethodNotImplemented, this);\n }\n\n /** Returns a default empty `OPTIONS` response. */\n protected async options(): Promise<Response> {\n return this.response(Options);\n }\n\n /**\n * Default handler for `HEAD` requests.\n * Performs a `GET` request and removes the body for `HEAD` semantics.\n *\n * Usually does not need to be overridden as this behavior covers\n * standard `HEAD` requirements.\n */\n protected async head(): Promise<Response> {\n const worker = this.create(\n new Request(this.request.url, { method: GET, headers: this.request.headers }),\n );\n return this.response(Head, await worker.fetch());\n }\n\n /**\n * The DEFAULT allowed HTTP methods for subclasses are `GET`, `HEAD`, `OPTIONS`.\n *\n * These defaults were selected for getting started quickly and can be\n * overridden for each worker subclass.\n */\n public getAllowedMethods(): Method[] {\n return [GET, HEAD, OPTIONS];\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 { match } from \"path-to-regexp\";\nimport { MatchedRoute, Route, PathParams, RouteTable } from \"./interfaces/route\";\nimport { Method } from \"./constants/methods\";\n\n/**\n * Container for route definitions and matching logic.\n * Implements Iterable to allow iteration over all routes.\n */\nexport class Routes implements Iterable<Route> {\n /** Internal array of registered routes */\n private readonly routes: Route[] = [];\n\n /**\n * Add routes to the router.\n *\n * Accepts any iterable of [method, path, handler] tuples.\n * This includes arrays, Sets, or generators.\n *\n * @param routes - Iterable of route tuples to add.\n */\n public add(routes: RouteTable): void {\n for (const [method, path, handler] of routes) {\n const matcher = match<PathParams>(path);\n this.routes.push({ method, matcher, handler });\n }\n }\n\n /**\n * Attempt to match a URL against the registered routes.\n *\n * @param method - HTTP method of the request\n * @param url - Full URL string to match against\n * @returns A MatchedRoute object if a route matches, otherwise null\n */\n public match(method: Method, url: string): MatchedRoute | null {\n const pathname = new URL(url).pathname;\n\n for (const route of this) {\n if (route.method !== method) continue;\n\n const found = route.matcher(pathname);\n if (found) return { route, params: found.params };\n }\n\n return null;\n }\n\n /**\n * Iterate over all registered routes.\n */\n public *[Symbol.iterator](): Iterator<Route> {\n yield* this.routes;\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 { BasicWorker } from \"./basic\";\nimport { NotFound } from \"../errors\";\nimport { Routes } from \"../routes\";\nimport { RouteHandler, RouteTable } from \"../interfaces/route\";\nimport { WorkerClass } from \"../interfaces/worker\";\nimport { BaseWorker } from \"./base\";\nimport { Method } from \"../constants/methods\";\n\n/**\n * Base worker supporting route-based request handling.\n *\n * Subclass `RouteWorker` to define a worker with multiple route handlers.\n *\n * Routes can be registered individually via `route()` or in bulk via `routes()`.\n */\nexport abstract class RouteWorker extends BasicWorker {\n /** Internal table of registered routes. */\n private readonly _routes: Routes = new Routes();\n\n /**\n * Registers a single new route in the worker.\n *\n * When a request matches the specified method and path, the provided handler\n * will be executed. The handler can be either:\n * - A function that receives URL parameters, or\n * - A Worker subclass that will handle the request.\n *\n * @param method - HTTP method for the route (`GET`, `POST`, etc.).\n * @param path - URL path pattern (Express-style, e.g., \"/users/:id\").\n * @param handler - The function or Worker class to run when the route matches.\n * @returns The current worker instance, allowing method chaining.\n */\n protected route(method: Method, path: string, handler: RouteHandler): this {\n this.routes([[method, path, handler]]);\n return this;\n }\n\n /**\n * Registers multiple routes at once in the worker.\n *\n * Each route should be a tuple `[method, path, handler]` where:\n * - `method` - HTTP method for the route (`GET`, `POST`, etc.).\n * - `path` - URL path pattern (Express-style, e.g., \"/users/:id\").\n * - `handler` - A function that receives URL parameters or a Worker subclass\n * that will handle the request.\n *\n * @param routes - An iterable of routes to register. Each item is a `[method, path, handler]` tuple.\n * @returns The current worker instance, allowing method chaining.\n */\n protected routes(routes: RouteTable): this {\n this._routes.add(routes);\n return this;\n }\n\n /**\n * Matches the incoming request against registered routes and dispatches it.\n *\n * If a route is found:\n * - If the handler is a Worker class, a new instance is created and its `fetch()` is called.\n * - If the handler is a callback function, it is invoked with the extracted path parameters.\n *\n * If no route matches, the request is passed to the parent `dispatch()` handler.\n *\n * @returns A `Promise<Response>` from the matched handler or parent dispatch.\n */\n protected override async dispatch(): Promise<Response> {\n const found = this._routes.match(this.request.method as Method, this.request.url);\n if (!found) return super.dispatch();\n\n const { handler } = found.route;\n if (RouteWorker.isWorkerClass(handler)) {\n return new handler(this.request, this.env, this.ctx).fetch();\n }\n return handler.call(this, found.params);\n }\n\n /**\n * Runtime type guard to check if a given handler is a Worker class.\n *\n * A Worker class is any class that extends `BaseWorker`.\n *\n * @param handler - The constructor function to test.\n * @returns `true` if `handler` is a subclass of `BaseWorker` at runtime, `false` otherwise.\n */\n private static isWorkerClass(handler: RouteHandler): handler is WorkerClass {\n return BaseWorker.prototype.isPrototypeOf(handler.prototype);\n }\n\n protected override async get(): Promise<Response> {\n return this.response(NotFound);\n }\n\n protected override async put(): Promise<Response> {\n return this.response(NotFound);\n }\n\n protected override async post(): Promise<Response> {\n return this.response(NotFound);\n }\n\n protected override async patch(): Promise<Response> {\n return this.response(NotFound);\n }\n\n protected override async delete(): Promise<Response> {\n return this.response(NotFound);\n }\n}\n"],"mappings":"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","names":["StatusCodes","CacheLib","CacheControl","Method","GET","PUT","HEAD","POST","PATCH","DELETE","OPTIONS","Time","Middleware","isStringArray","value","item","isString","isFunction","isNumber","isBoolean","assertCacheName","value","isString","assertGetKey","isFunction","assertKey","StatusCodes","HttpHeader","FORBIDDEN_304_HEADERS","FORBIDDEN_204_HEADERS","lexCompare","a","b","setHeader","headers","key","value","raw","values","v","lexCompare","mergeHeader","merged","getHeaderValues","filterHeaders","keys","VARY_CACHE_URL","isCacheable","response","StatusCodes","getVaryHeader","values","getHeaderValues","HttpHeader","v","lexCompare","filterVaryHeader","vary","h","value","getVaryKey","request","key","varyPairs","filtered","header","encoded","base64UrlEncode","str","utf8","binary","byte","cache","cacheName","getKey","assertCacheName","assertGetKey","CacheHandler","sortSearchParams","request","url","sorted","a","b","lexCompare","stripSearchParams","Middleware","worker","next","GET","cached","response","vary","key","getVaryKey","isCacheable","filterVaryHeader","getVaryHeader","assertKey","METHOD_SET","Method","isMethod","value","isString","isMethodArray","assertMethods","desc","getReasonPhrase","StatusCodes","UTF8_CHARSET","withCharset","mediaType","charset","assertOctetStreamInit","value","obj","size","isNumber","offset","length","BaseResponse","StatusCodes","withCharset","UTF8_CHARSET","getReasonPhrase","key","value","setHeader","mergeHeader","HttpHeader","filterHeaders","FORBIDDEN_204_HEADERS","FORBIDDEN_304_HEADERS","CacheResponse","cache","CacheControl","WorkerResponse","body","ClonedResponse","response","clone","SuccessResponse","status","JsonResponse","json","HtmlResponse","charset","TextResponse","OctetStream","_OctetStream","stream","init","assertOctetStreamInit","normalized","size","offset","length","R2ObjectStream","_R2ObjectStream","source","useCache","range","WebSocketUpgrade","client","Head","get","Options","ALLOW_ALL_ORIGINS","SIMPLE_METHODS","GET","HEAD","OPTIONS","SKIP_CORS_STATUSES","StatusCodes","defaultCorsConfig","HttpHeader","Time","options","worker","cors","Options","origin","getOrigin","setAllowOrigin","setAllowCredentials","setAllowMethods","setAllowHeaders","setMaxAge","apply","response","clone","ClonedResponse","deleteCorsHeaders","setExposedHeaders","headers","allowAllOrigins","setHeader","HttpHeader","ALLOW_ALL_ORIGINS","mergeHeader","methods","assertMethods","allowed","method","SIMPLE_METHODS","maxAge","skipCors","status","SKIP_CORS_STATUSES","request","assertCorsInit","value","obj","isStringArray","isBoolean","isNumber","cors","init","assertCorsInit","CorsHandler","Middleware","defaultCorsConfig","worker","next","OPTIONS","options","response","skipCors","apply","getReasonPhrase","StatusCodes","WS_UPGRADE","WS_WEBSOCKET","CloseCode","WS_RESERVED_CODES","HttpError","JsonResponse","status","details","json","getReasonPhrase","CacheControl","BadRequest","StatusCodes","Unauthorized","Forbidden","NotFound","MethodNotAllowed","worker","methods","assertMethods","HttpHeader","UpgradeRequired","InternalServerError","NotImplemented","MethodNotImplemented","ServiceUnavailable","hasConnectionHeader","headers","getHeaderValues","HttpHeader","value","WS_UPGRADE","hasUpgradeHeader","WS_WEBSOCKET","hasWebSocketVersion","websocket","path","WebSocketHandler","Middleware","worker","next","GET","headers","hasConnectionHeader","hasUpgradeHeader","hasWebSocketVersion","UpgradeRequired","BadRequest","request","isBinary","value","isSendable","isString","safeCloseCode","code","isNumber","isCodeInRange","isReservedCode","CloseCode","WS_RESERVED_CODES","safeReason","reason","assertSerializable","WebSocketEvents","_WebSocketEvents","type","server","listener","options","arr","finalOptions","index","ev","once","listeners","msg","BaseWebSocket","WebSocketEvents","server","data","isSendable","attachment","merged","assertSerializable","states","code","reason","safeCloseCode","safeReason","event","NewConnectionBase","BaseWebSocket","pair","client","server","ctx","tags","RestoredConnectionBase","BaseWebSocket","ws","WebSocketSessions","attachment","NewConnection","NewConnectionBase","sessions","ctx","tags","connection","ws","RestoredConnection","RestoredConnectionBase","restore","all","restored","code","reason","con","BaseWorker","_request","_env","_ctx","method","methods","assertMethods","isMethod","request","ctor","ResponseClass","args","env","ctx","assertMiddleware","handler","Middleware","MiddlewareWorker","BaseWorker","handler","assertMiddleware","next","BasicWorker","MiddlewareWorker","MethodNotAllowed","error","InternalServerError","method","MethodNotImplemented","Options","worker","GET","Head","HEAD","OPTIONS","match","Routes","routes","method","path","handler","matcher","url","pathname","route","found","RouteWorker","_RouteWorker","BasicWorker","Routes","method","path","handler","routes","found","BaseWorker","NotFound"]}
1
+ {"version":3,"sources":["../src/constants/index.ts","../src/constants/cache.ts","../src/constants/methods.ts","../src/constants/time.ts","../src/guards/basic.ts","../src/guards/cache.ts","../src/middleware/cache/utils.ts","../src/constants/headers.ts","../src/utils/compare.ts","../src/utils/headers.ts","../src/middleware/cache/policy.ts","../src/middleware/cache/rules/get.ts","../src/middleware/cache/rules/utils.ts","../src/middleware/cache/rules/range.ts","../src/errors.ts","../src/responses.ts","../src/constants/media.ts","../src/utils/media.ts","../src/guards/responses.ts","../src/guards/methods.ts","../src/constants/websocket.ts","../src/middleware/cache/rules/etag.ts","../src/middleware/cache/rules/modified.ts","../src/middleware/cache/rules/control.ts","../src/middleware/cache/keys.ts","../src/middleware/cache/handler.ts","../src/middleware/cors/constants.ts","../src/middleware/cors/utils.ts","../src/guards/cors.ts","../src/middleware/cors/handler.ts","../src/middleware/websocket/utils.ts","../src/middleware/websocket/handler.ts","../src/guards/websocket.ts","../src/websocket/events.ts","../src/websocket/base.ts","../src/websocket/new.ts","../src/websocket/restore.ts","../src/websocket/sessions.ts","../src/workers/base.ts","../src/guards/middleware.ts","../src/workers/middleware.ts","../src/workers/basic.ts","../src/routes.ts","../src/workers/route.ts"],"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 * https://github.com/prettymuchbryce/http-status-codes\n */\nexport { StatusCodes } from \"http-status-codes\";\n\nexport * from \"./cache\";\nexport * from \"./methods\";\nexport * from \"./time\";\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 * 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\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 * 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 * Checks if a value is a function.\n *\n * @param value - The value to check.\n * @returns `true` if the value is a function, otherwise `false`.\n */\nexport function isFunction(value: unknown): value is Function {\n return typeof value === \"function\";\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 { isFunction, isString } from \"./basic\";\n\n/**\n * Asserts that a value is a string suitable for a cache name.\n *\n * If the value is `undefined`, this function does nothing.\n * Otherwise, it throws a `TypeError` if the value is not a string.\n *\n * @param value - The value to check.\n * @throws TypeError If the value is defined but not a string.\n */\nexport function assertCacheName(value: unknown): asserts value is string | undefined {\n if (value === undefined) return;\n if (!isString(value)) {\n throw new TypeError(\"Cache name must be a string.\");\n }\n}\n\n/**\n * Asserts that a value is a function suitable for `getKey`.\n *\n * If the value is `undefined`, this function does nothing.\n * Otherwise, it throws a `TypeError` if the value is not a function.\n *\n * @param value - The value to check.\n * @throws TypeError If the value is defined but not a function.\n */\nexport function assertGetKey(value: unknown): asserts value is Function | undefined {\n if (value === undefined) return;\n if (!isFunction(value)) {\n throw new TypeError(\"getKey must be a function.\");\n }\n}\n\n/**\n * Asserts that a value is a `URL` instance suitable for use as a cache key.\n *\n * If the value is not a `URL`, this function throws a `TypeError`.\n *\n * @param value - The value to check.\n * @throws TypeError If the value is not a `URL`.\n */\nexport function assertKey(value: unknown): asserts value is URL {\n if (!(value instanceof URL)) {\n throw new TypeError(\"getKey must return a URL.\");\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 *\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\";\nimport { HttpHeader } from \"../../constants/headers\";\nimport { lexCompare } from \"../../utils/compare\";\nimport { getHeaderValues } from \"../../utils/headers\";\nimport { VARY_WILDCARD } from \"./constants\";\nimport { GET } from \"../../constants\";\n\n/** Base URL used for constructing cache keys. Only used internally. */\nconst VARY_CACHE_URL = \"https://vary\";\n\n/**\n * Determines whether a Response is cacheable.\n * - Request method must be GET\n * - Request cache cannot be `no-store`\n * - Status must be 200 OK\n * - Must not contain a Vary header with a wildcard (`*`)\n *\n * @param response The Response object to check.\n * @returns `true` if the response can be cached; `false` otherwise.\n */\nexport function isCacheable(request: Request, response: Response): boolean {\n if (request.method !== GET) return false;\n if (request.cache === \"no-store\") return false;\n if (response.status !== StatusCodes.OK) return false;\n if (getVaryHeader(response).includes(VARY_WILDCARD)) return false;\n\n return true;\n}\n\n/**\n * Extracts and normalizes the `Vary` header from a Response.\n * - Splits comma-separated values\n * - Deduplicates\n * - Converts all values to lowercase\n * - Sorts lexicographically\n *\n * @param response The Response object containing headers.\n * @returns An array of normalized header names from the Vary header.\n */\nexport function getVaryHeader(response: Response): string[] {\n const values = getHeaderValues(response.headers, HttpHeader.VARY);\n return Array.from(new Set(values.map((v) => v.toLowerCase()))).sort(lexCompare);\n}\n\n/**\n * Filters out headers that should be ignored for caching, currently:\n * - `Accept-Encoding` (handled automatically by the platform)\n *\n * @param vary Array of normalized Vary header names.\n * @returns Array of headers used for computing cache variations.\n */\nexport function filterVaryHeader(vary: string[]): string[] {\n return vary\n .map((h) => h.toLowerCase())\n .filter((value) => value !== HttpHeader.ACCEPT_ENCODING.toLowerCase());\n}\n\n/**\n * Generates a Vary-aware cache key for a request.\n *\n * The key is based on:\n * 1. The provided `key` URL, which is normalized by default but can be fully customized\n * by the caller. For example, users can:\n * - Sort query parameters\n * - Remove the search/query string entirely\n * - Exclude certain query parameters\n * This allows full control over how this cache key is generated.\n * 2. The request headers listed in `vary` (after filtering and lowercasing).\n *\n * Behavior:\n * - Headers in `vary` are sorted and included in the key.\n * - The combination of the key URL and header values is base64-encoded to produce\n * a safe cache key.\n * - The resulting string is returned as an absolute URL rooted at `VARY_CACHE_URL`.\n *\n * @param request The Request object to generate a key for.\n * @param vary Array of header names from the `Vary` header that affect caching.\n * @param key The cache key to be used for this request. Can be modified by the caller for\n * custom cache key behavior.\n * @returns A string URL representing a unique cache key for this request + Vary headers.\n */\nexport function getVaryKey(request: Request, vary: string[], key: URL): string {\n const varyPairs: [string, string][] = [];\n const filtered = filterVaryHeader(vary);\n\n filtered.sort(lexCompare);\n for (const header of filtered) {\n const value = request.headers.get(header);\n if (value !== null) {\n varyPairs.push([header, value]);\n }\n }\n\n const encoded = base64UrlEncode(JSON.stringify([key.toString(), varyPairs]));\n return new URL(encoded, VARY_CACHE_URL).href;\n}\n\n/**\n * Encodes a string as URL-safe Base64.\n * - Converts to UTF-8 bytes\n * - Base64-encodes\n * - Replaces `+` with `-` and `/` with `_`\n * - Removes trailing `=`\n *\n * @param str The input string to encode.\n * @returns URL-safe Base64 string.\n */\nexport function base64UrlEncode(str: string): string {\n const utf8 = new TextEncoder().encode(str);\n let binary = \"\";\n for (const byte of utf8) {\n binary += String.fromCodePoint(byte);\n }\n return btoa(binary)\n .replaceAll(\"+\", \"-\")\n .replaceAll(\"/\", \"_\")\n .replace(/={1,2}$/, \"\");\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 * Internally used headers.\n */\nexport namespace HttpHeader {\n export const ACCEPT_ENCODING = \"Accept-Encoding\";\n export const ACCEPT_RANGES = \"Accept-Ranges\";\n export const ALLOW = \"Allow\";\n export const CACHE_CONTROL = \"Cache-Control\";\n export const CONNECTION = \"Connection\";\n export const CONTENT_DISPOSITION = \"Content-Disposition\";\n export const CONTENT_ENCODING = \"Content-Encoding\";\n export const CONTENT_LANGUAGE = \"Content-Language\";\n export const CONTENT_LENGTH = \"Content-Length\";\n export const CONTENT_RANGE = \"Content-Range\";\n export const CONTENT_TYPE = \"Content-Type\";\n export const CONTENT_MD5 = \"Content-MD5\";\n export const ETAG = \"ETag\";\n export const IF_MODIFIED_SINCE = \"If-Modified-Since\";\n export const IF_MATCH = \"If-Match\";\n export const IF_NONE_MATCH = \"If-None-Match\";\n export const LAST_MODIFIED = \"Last-Modified\";\n export const ORIGIN = \"Origin\";\n export const RANGE = \"Range\";\n export const VARY = \"Vary\";\n\n // Cors Headers\n export const ACCESS_CONTROL_ALLOW_CREDENTIALS = \"Access-Control-Allow-Credentials\";\n export const ACCESS_CONTROL_ALLOW_HEADERS = \"Access-Control-Allow-Headers\";\n export const ACCESS_CONTROL_ALLOW_METHODS = \"Access-Control-Allow-Methods\";\n export const ACCESS_CONTROL_ALLOW_ORIGIN = \"Access-Control-Allow-Origin\";\n export const ACCESS_CONTROL_EXPOSE_HEADERS = \"Access-Control-Expose-Headers\";\n export const ACCESS_CONTROL_MAX_AGE = \"Access-Control-Max-Age\";\n\n // Websocket Headers\n export const SEC_WEBSOCKET_VERSION = \"Sec-WebSocket-Version\";\n export const UPGRADE = \"Upgrade\";\n}\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\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 * 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\";\nimport { Worker } from \"../../interfaces/worker\";\nimport { CacheRule } from \"./rules/interfaces\";\n\nexport class CachePolicy {\n private rules: CacheRule[] = [];\n\n public use(...rules: CacheRule[]): this {\n this.rules.push(...rules);\n return this;\n }\n\n public async execute(\n worker: Worker,\n getCached: () => Promise<Response | undefined>,\n ): Promise<Response | undefined> {\n const chain = this.rules.reduceRight(\n (next, rule) => async () => {\n const response = await next();\n if (!response) return undefined;\n if (response.status !== StatusCodes.OK) return response;\n\n return rule.handle(worker, async () => response);\n },\n () => getCached(),\n );\n\n return await chain();\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 { GET } from \"../../../constants\";\nimport { Worker } from \"../../../interfaces\";\nimport { CacheRule } from \"./interfaces\";\n\nexport class GetMethodRule implements CacheRule {\n public async handle(\n worker: Worker,\n next: () => Promise<Response>,\n ): Promise<Response | undefined> {\n if (worker.request.method !== GET) return undefined;\n\n return next();\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 { getHeaderValues } from \"../../../utils/headers\";\nimport { ByteRange, CacheValidators } from \"./interfaces\";\nimport { CacheControl } from \"../../../constants\";\n\nconst RANGE_REGEX = /^bytes=(\\d{1,12})-(\\d{0,12})$/;\n\n/**\n * Parses the `Range` header from an HTTP request and returns a byte range object.\n *\n * Only supports **single-range headers** of the form `bytes=X-Y` or `bytes=X-`.\n * - `X` (start) is **required** and must be a whole number (up to 12 digits).\n * - `Y` (end) is optional; if missing, `end` is `undefined`.\n *\n * @param request - The HTTP request object containing headers\n * @returns A ByteRange object with `start` and optional `end` if a valid range is found,\n * otherwise `undefined`.\n */\nexport function getRange(request: Request): ByteRange | undefined {\n const range = request.headers.get(\"range\");\n if (!range) return;\n\n const match = RANGE_REGEX.exec(range);\n if (!match) return;\n\n const start = Number(match[1]);\n const end = match[2] === \"\" ? undefined : Number(match[2]);\n\n return end !== undefined ? { start, end } : { start };\n}\n\n/** Normalizes an ETag for weak comparison (strips \"W/\" prefix). */\nexport function normalizeWeak(etag: string): string {\n return etag.startsWith(\"W/\") ? etag.slice(2) : etag;\n}\n\n/** Normalizes an ETag for strong comparison (no changes). */\nexport function normalizeStrong(etag: string): string {\n return etag;\n}\n\n/**\n * Parses the Cache-Control header from the given headers.\n *\n * @param headers - The request headers to inspect.\n * @returns A `CacheControl` object.\n */\nexport function getCacheControl(headers: Headers): CacheControl {\n return CacheControl.parse(headers.get(HttpHeader.CACHE_CONTROL) ?? \"\");\n}\n\n/**\n * Extracts cache validators from request headers.\n *\n * Cache validators allow conditional requests against cached resources.\n * They let clients revalidate instead of always re-downloading.\n *\n * Recognized validators:\n * - `If-None-Match` (ETag weak comparison)\n * - `If-Match` (ETag strong comparison)\n * - `If-Modified-Since` (date-based comparison)\n *\n * @param headers - The request headers to inspect.\n * @returns Object containing the parsed cache validators.\n */\nexport function getCacheValidators(headers: Headers): CacheValidators {\n return {\n ifNoneMatch: getHeaderValues(headers, HttpHeader.IF_NONE_MATCH),\n ifMatch: getHeaderValues(headers, HttpHeader.IF_MATCH),\n ifModifiedSince: headers.get(HttpHeader.IF_MODIFIED_SINCE),\n };\n}\n\n/**\n * Returns true if any cache validator headers are present.\n *\n * Useful as a quick check for conditional requests where the\n * specific values are not important.\n *\n * @param headers - The request headers to inspect.\n * @returns `true` if any validator exists, otherwise `false`.\n */\nexport function hasCacheValidator(headers: Headers): boolean {\n const { ifNoneMatch, ifMatch, ifModifiedSince } = getCacheValidators(headers);\n return ifNoneMatch.length > 0 || ifMatch.length > 0 || ifModifiedSince !== null;\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 { isNumber } from \"../../../guards/basic\";\nimport { Worker } from \"../../../interfaces/worker\";\nimport { CacheRule } from \"./interfaces\";\nimport { getRange } from \"./utils\";\n\nexport class RangeRule implements CacheRule {\n public async handle(\n worker: Worker,\n next: () => Promise<Response>,\n ): Promise<Response | undefined> {\n const range = getRange(worker.request);\n\n if (range && (range.start !== 0 || range.end === 0)) {\n return undefined;\n }\n\n const response = await next();\n\n if (!range) return response;\n if (range.end === undefined) return response;\n\n // Validate response length\n const lengthHeader = response.headers.get(HttpHeader.CONTENT_LENGTH);\n const length = Number(lengthHeader);\n if (!isNumber(length) || range.end >= length) return undefined;\n\n return response;\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 { getReasonPhrase, StatusCodes } from \"http-status-codes\";\nimport { JsonResponse, WorkerResponse } from \"./responses\";\nimport { ErrorJson } from \"./interfaces/error\";\nimport { Worker } from \"./interfaces/worker\";\nimport { CacheControl } from \"./constants/cache\";\nimport { HttpHeader } from \"./constants/headers\";\nimport { assertMethods } from \"./guards/methods\";\nimport { WS_VERSION } from \"./constants/websocket\";\n\n/**\n * Generic HTTP error response.\n * Sends a JSON body with status, error message, and details.\n */\nexport class HttpError extends JsonResponse {\n /**\n * @param worker The worker handling the request.\n * @param status HTTP status code.\n * @param details Optional detailed error message.\n */\n constructor(\n status: StatusCodes,\n protected readonly details?: string,\n ) {\n const json: ErrorJson = {\n status,\n error: getReasonPhrase(status),\n details: details ?? \"\",\n };\n super(json, CacheControl.DISABLE, status);\n }\n}\n\n/** 400 Bad Request error response. */\nexport class BadRequest extends HttpError {\n constructor(details?: string) {\n super(StatusCodes.BAD_REQUEST, details);\n }\n}\n\n/** 401 Unauthorized error response. */\nexport class Unauthorized extends HttpError {\n constructor(details?: string) {\n super(StatusCodes.UNAUTHORIZED, details);\n }\n}\n\n/** 403 Forbidden error response. */\nexport class Forbidden extends HttpError {\n constructor(details?: string) {\n super(StatusCodes.FORBIDDEN, details);\n }\n}\n\n/** 404 Not Found error response. */\nexport class NotFound extends HttpError {\n constructor(details?: string) {\n super(StatusCodes.NOT_FOUND, details);\n }\n}\n\n/** 405 Method Not Allowed error response. */\nexport class MethodNotAllowed extends HttpError {\n constructor(worker: Worker) {\n const methods = worker.getAllowedMethods();\n assertMethods(methods);\n\n super(StatusCodes.METHOD_NOT_ALLOWED, `${worker.request.method} method not allowed.`);\n this.setHeader(HttpHeader.ALLOW, methods);\n }\n}\n\n/** 412 Precondition Failed error response */\nexport class PreconditionFailed extends WorkerResponse {\n constructor() {\n super();\n this.status = StatusCodes.PRECONDITION_FAILED;\n }\n}\n\n/** 426 Upgrade Required error response. */\nexport class UpgradeRequired extends HttpError {\n constructor() {\n super(StatusCodes.UPGRADE_REQUIRED);\n this.headers.set(HttpHeader.SEC_WEBSOCKET_VERSION, WS_VERSION);\n }\n}\n\n/** 500 Internal Server Error response. */\nexport class InternalServerError extends HttpError {\n constructor(details?: string) {\n super(StatusCodes.INTERNAL_SERVER_ERROR, details);\n }\n}\n\n/** 501 Not Implemented error response. */\nexport class NotImplemented extends HttpError {\n constructor(details?: string) {\n super(StatusCodes.NOT_IMPLEMENTED, details);\n }\n}\n\n/** 501 Method Not Implemented error response for unsupported HTTP methods. */\nexport class MethodNotImplemented extends NotImplemented {\n constructor(worker: Worker) {\n super(`${worker.request.method} method not implemented.`);\n }\n}\n\n/** 503 Service Unavailable error response. */\nexport class ServiceUnavailable extends HttpError {\n constructor(details?: string) {\n super(StatusCodes.SERVICE_UNAVAILABLE, details);\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 { getReasonPhrase, StatusCodes } from \"http-status-codes\";\nimport { CacheControl } from \"./constants/cache\";\nimport { setHeader, mergeHeader, filterHeaders } from \"./utils/headers\";\nimport { UTF8_CHARSET, MediaType } from \"./constants/media\";\nimport { FORBIDDEN_204_HEADERS, FORBIDDEN_304_HEADERS, HttpHeader } from \"./constants/headers\";\nimport { OctetStreamInit } from \"./interfaces/response\";\nimport { withCharset } from \"./utils/media\";\nimport { assertOctetStreamInit } from \"./guards/responses\";\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 /** Enable websocket responses. */\n public webSocket: WebSocket | null = 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 * Wraps an existing Response and clones its body, headers, and status.\n */\nexport class ClonedResponse extends WorkerResponse {\n constructor(response: Response, cache?: CacheControl) {\n const clone = response.clone();\n super(clone.body, cache);\n this.headers = new Headers(clone.headers);\n this.status = clone.status;\n this.statusText = clone.statusText;\n }\n}\n\n/**\n * Copies the response, but with null body and status 304 Not Modified.\n */\nexport class NotModified extends ClonedResponse {\n constructor(response: Response) {\n super(response);\n this.status = StatusCodes.NOT_MODIFIED;\n this.statusText = getReasonPhrase(StatusCodes.NOT_MODIFIED);\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(null);\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` preflight requests.\n */\nexport class Options extends WorkerResponse {\n constructor() {\n super();\n this.status = StatusCodes.NO_CONTENT;\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 * 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 { OctetStreamInit } from \"../interfaces\";\nimport { isNumber } from \"./basic\";\n\n/**\n * Asserts that a given value is a valid `OctetStreamInit` object.\n *\n * Properties:\n * - `size` (required): must be a non-negative number.\n * - `offset` (optional): if provided, must be a number between 0 and `size`.\n * - `length` (optional): if provided, must be a non-negative number such that `offset + length <= size`.\n *\n * If `offset` or `length` are `undefined`, they are considered as `0` and `size` respectively.\n *\n * Throws an error if validation fails.\n *\n * Acts as a TypeScript type predicate, so after calling it, `value` is narrowed to `OctetStreamInit`.\n *\n * @param value - The value to validate as `OctetStreamInit`.\n * @throws {TypeError} If the value is not a non-null object.\n * @throws {RangeError} If `size`, `offset`, or `length` are invalid.\n * @returns `true` if the value is a valid `OctetStreamInit`.\n */\nexport function assertOctetStreamInit(value: unknown): asserts value is OctetStreamInit {\n if (typeof value !== \"object\" || value === null) {\n throw new TypeError(\"OctetStreamInit must be an object.\");\n }\n\n const obj = value as Record<string, unknown>;\n\n // size\n const size = obj[\"size\"];\n if (!isNumber(size) || size < 0 || !Number.isInteger(size)) {\n throw new RangeError(\n `OctetStreamInit.size must be a non-negative integer (size=${JSON.stringify(size)}).`,\n );\n }\n\n // offset\n const offset = obj[\"offset\"] ?? 0;\n if (!isNumber(offset) || offset < 0 || offset > size || !Number.isInteger(offset)) {\n throw new RangeError(\n `OctetStreamInit.offset must be a non-negative integer less than or equal to size (size=${JSON.stringify(size)}, offset=${JSON.stringify(offset)}).`,\n );\n }\n\n // length\n const length = obj[\"length\"] ?? size - offset;\n if (!isNumber(length) || length < 0 || offset + length > size || !Number.isInteger(length)) {\n throw new RangeError(\n `OctetStreamInit.length must be a non-negative integer less than or equal to size - offset (size=${JSON.stringify(size)}, offset=${JSON.stringify(offset)}, length=${JSON.stringify(length)}).`,\n );\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 { Method } from \"../constants/methods\";\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 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\n/** WebSocket upgrade header value */\nexport const WS_UPGRADE = \"upgrade\";\n/** WebSocket protocol header value */\nexport const WS_WEBSOCKET = \"websocket\";\n/** WebSocket protocol version */\nexport const WS_VERSION = \"13\";\n/** Max close code a user can send */\nexport const WS_MAX_CLOSE_CODE = 4999;\n/** Max number of reason chars a user can send */\nexport const WS_MAX_REASON_CHARS = 123;\n\n/** WebSocket close codes */\nexport const CloseCode = {\n NORMAL: 1000,\n GOING_AWAY: 1001,\n PROTOCOL_ERROR: 1002,\n UNSUPPORTED_DATA: 1003,\n NO_STATUS: 1005,\n ABNORMAL: 1006,\n INVALID_PAYLOAD: 1007,\n POLICY_VIOLATION: 1008,\n MESSAGE_TOO_BIG: 1009,\n MISSING_EXTENSION: 1010,\n INTERNAL_ERROR: 1011,\n TLS_HANDSHAKE: 1015,\n} as const;\n\n/** WebSocket RESERVED close codes */\nexport const WS_RESERVED_CODES = new Set<number>([\n CloseCode.NO_STATUS,\n CloseCode.ABNORMAL,\n CloseCode.TLS_HANDSHAKE,\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 { Worker } from \"../../../interfaces\";\nimport { HttpHeader } from \"../../../constants/headers\";\nimport { PreconditionFailed } from \"../../../errors\";\nimport { NotModified } from \"../../../responses\";\nimport { CacheRule } from \"./interfaces\";\nimport { getCacheValidators, normalizeStrong, normalizeWeak } from \"./utils\";\n\n/**\n * Cache rule that handles conditional GETs based on ETag headers.\n * - Applies If-Match (strong comparison) and If-None-Match (weak comparison) rules.\n * - Returns `undefined` if the cached response cannot be used.\n * - Returns `NotModified` or `PreconditionFailed` responses when appropriate.\n */\nexport class ETagRule implements CacheRule {\n public async handle(\n worker: Worker,\n next: () => Promise<Response>,\n ): Promise<Response | undefined> {\n const response = await next();\n\n const etag = response.headers.get(HttpHeader.ETAG);\n if (!etag) return response;\n\n const { ifNoneMatch, ifMatch } = getCacheValidators(worker.request.headers);\n\n // If-Match (strong comparison)\n if (ifMatch.length > 0 && !ifMatch.includes(\"*\")) {\n const normalizedEtag = normalizeStrong(etag);\n const normalizedMatches = ifMatch.map(normalizeStrong);\n if (!normalizedMatches.includes(normalizedEtag)) {\n return new PreconditionFailed().response();\n }\n }\n\n // If-None-Match (weak comparison or wildcard)\n if (ifNoneMatch.length > 0) {\n const normalizedEtag = normalizeWeak(etag);\n const normalizedMatches = ifNoneMatch.map(normalizeWeak);\n if (ifNoneMatch.includes(\"*\") || normalizedMatches.includes(normalizedEtag)) {\n return new NotModified(response).response();\n }\n\n // ETags exist but don't match → cache cannot be used\n return undefined;\n }\n\n return response;\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 { Worker } from \"../../../interfaces/worker\";\nimport { NotModified } from \"../../../responses\";\nimport { CacheRule } from \"./interfaces\";\nimport { getCacheValidators } from \"./utils\";\n\nexport class LastModifiedRule implements CacheRule {\n public async handle(\n worker: Worker,\n next: () => Promise<Response>,\n ): Promise<Response | undefined> {\n const response = await next();\n\n const lastModified = response.headers.get(HttpHeader.LAST_MODIFIED);\n const { ifModifiedSince } = getCacheValidators(worker.request.headers);\n\n if (!lastModified || !ifModifiedSince) {\n return response;\n }\n\n const lastModifiedTime = Date.parse(lastModified);\n const ifModifiedSinceTime = Date.parse(ifModifiedSince);\n\n if (isNaN(lastModifiedTime) || isNaN(ifModifiedSinceTime)) {\n return response;\n }\n\n if (lastModifiedTime <= ifModifiedSinceTime) {\n return new NotModified(response).response();\n }\n\n return response;\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 { Worker } from \"../../../interfaces\";\nimport { CacheRule } from \"./interfaces\";\nimport { getCacheControl, hasCacheValidator } from \"./utils\";\n\nexport class CacheControlRule implements CacheRule {\n public async handle(\n worker: Worker,\n next: () => Promise<Response>,\n ): Promise<Response | undefined> {\n const cache = getCacheControl(worker.request.headers);\n\n if (Boolean(cache[\"no-store\"])) {\n return undefined;\n }\n\n if (Boolean(cache[\"no-cache\"]) && !hasCacheValidator(worker.request.headers)) {\n return undefined;\n }\n\n return next();\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 *\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 \"../../utils/compare\";\n\n/**\n * Returns a new URL with its query parameters sorted into a stable order.\n *\n * This is used for cache key generation: URLs that differ only in the\n * order of their query parameters will normalize to the same key.\n *\n * @param request - The incoming Request whose URL will be normalized.\n * @returns A new URL with query parameters sorted by name.\n */\nexport function sortSearchParams(request: Request): URL {\n const url = new URL(request.url);\n const sorted = new URLSearchParams(\n [...url.searchParams.entries()].sort(([a], [b]) => lexCompare(a, b)),\n );\n url.search = sorted.toString();\n url.hash = \"\";\n return url;\n}\n\n/**\n * Returns a new URL with all query parameters removed.\n *\n * This is used when query parameters are not relevant to cache lookups,\n * ensuring that variants of the same resource share a single cache entry.\n *\n * @param request - The incoming Request whose URL will be normalized.\n * @returns A new URL with no query parameters.\n */\nexport function stripSearchParams(request: Request): URL {\n const url = new URL(request.url);\n url.search = \"\";\n url.hash = \"\";\n return url;\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 { Worker } from \"../../interfaces/worker\";\nimport { assertCacheName, assertGetKey, assertKey } from \"../../guards/cache\";\nimport { filterVaryHeader, getVaryHeader, getVaryKey, isCacheable } from \"./utils\";\nimport { CachePolicy } from \"./policy\";\nimport { GetMethodRule } from \"./rules/get\";\nimport { RangeRule } from \"./rules/range\";\nimport { ETagRule } from \"./rules/etag\";\nimport { LastModifiedRule } from \"./rules/modified\";\nimport { CacheControlRule } from \"./rules/control\";\nimport { Middleware } from \"../../interfaces/middleware\";\nimport { sortSearchParams } from \"./keys\";\n\n/**\n * Creates a Vary-aware caching middleware for Workers.\n *\n * This middleware:\n * - Caches `GET` requests **only**.\n * - Respects the `Vary` header of responses, ensuring that requests\n * with different headers (e.g., `Origin`) receive the correct cached response.\n * - Skips caching for non-cacheable responses (e.g., error responses or\n * responses with `Vary: *`).\n *\n * @param cacheName Optional name of the cache to use. If omitted, the default cache is used.\n * @param getKey Optional function to compute a custom cache key from a request.\n * If omitted, the request URL is normalized and used as the key.\n * @returns A `Middleware` instance that can be used in a Worker pipeline.\n */\nexport function cache(cacheName?: string, getKey?: (request: Request) => URL): Middleware {\n assertCacheName(cacheName);\n assertGetKey(getKey);\n\n return new CacheHandler(cacheName, getKey);\n}\n\n/**\n * Cache Middleware Implementation\n * @see {@link cache}\n */\nclass CacheHandler implements Middleware {\n constructor(\n private readonly cacheName?: string,\n private readonly getKey?: (request: Request) => URL,\n ) {\n this.cacheName = cacheName?.trim() || undefined;\n }\n\n /**\n * Handles an incoming request through the cache middleware.\n *\n * Behavior:\n * - Opens the configured cache (or the default cache if none specified).\n * - Creates a `CachePolicy` with default rules (GET check, range check, ETag handling).\n * - Executes the policy to determine if a cached response can be returned.\n * - If a cached response is found and valid per the rules, it is returned.\n * - If no cached response is usable, the `next()` handler is invoked to fetch a fresh response.\n * - Stores the fresh response in the cache if it is cacheable.\n *\n * @param worker - The Worker instance containing the request and context.\n * @param next - Function to invoke the next middleware or origin fetch.\n * @returns A `Response` object, either from cache or freshly fetched.\n */\n public async handle(worker: Worker, next: () => Promise<Response>): Promise<Response> {\n const cache = this.cacheName ? await caches.open(this.cacheName) : caches.default;\n\n const policy = new CachePolicy()\n .use(new GetMethodRule())\n .use(new CacheControlRule())\n .use(new RangeRule())\n .use(new ETagRule())\n .use(new LastModifiedRule());\n\n const cacheResponse = await policy.execute(worker, () =>\n this.getCached(cache, worker.request),\n );\n if (cacheResponse) return cacheResponse;\n\n const response = await next();\n\n this.setCached(cache, worker, response);\n return response;\n }\n\n /**\n * Retrieves a cached response for a given request.\n * - Checks both the base cache key and any Vary-specific keys.\n *\n * @param cache The Cache object to check.\n * @param request The request to retrieve a cached response for.\n * @returns A cached Response if available, otherwise `undefined`.\n */\n public async getCached(cache: Cache, request: Request): Promise<Response | undefined> {\n const url = this.getCacheKey(request);\n const response = await cache.match(url);\n if (!response) return undefined;\n\n const vary = this.getFilteredVary(response);\n if (vary.length === 0) return response;\n\n const key = getVaryKey(request, vary, url);\n return cache.match(key);\n }\n\n /**\n * Caches a response if it is cacheable.\n *\n * Behavior:\n * - Always stores the response under the main cache key. This ensures that\n * the response’s Vary headers are available for later cache lookups.\n * - If the response varies based on certain request headers (per the Vary header),\n * also stores a copy under a Vary-specific cache key so future requests\n * with matching headers can retrieve the correct response.\n *\n * @param cache The Cache object to store the response in.\n * @param worker The Worker instance containing the request and context.\n * @param response The Response to cache.\n */\n public async setCached(cache: Cache, worker: Worker, response: Response): Promise<void> {\n if (!isCacheable(worker.request, response)) return;\n\n const url = this.getCacheKey(worker.request);\n\n // Always store the main cache entry to preserve Vary headers\n worker.ctx.waitUntil(cache.put(url, response.clone()));\n\n // Store request-specific cache entry if the response varies\n const vary = this.getFilteredVary(response);\n if (vary.length > 0) {\n worker.ctx.waitUntil(\n cache.put(getVaryKey(worker.request, vary, url), response.clone()),\n );\n }\n }\n\n /**\n * Extracts and filters the `Vary` header from a response.\n *\n * @param response - The HTTP response to inspect.\n * @returns An array of filtered header names from the `Vary` header.\n */\n public getFilteredVary(response: Response): string[] {\n return filterVaryHeader(getVaryHeader(response));\n }\n\n /**\n * Returns the cache key for a request.\n *\n * By default, this is a normalized URL including the path and query string.\n * However, users can provide a custom `getKey` function when creating the\n * `cache` middleware to fully control how the cache keys are generated.\n *\n * For example, a custom function could:\n * - Sort or remove query parameters\n * - Exclude the search/query string entirely\n * - Modify the path or host\n *\n * This allows complete flexibility over cache key generation.\n *\n * @param request The Request object to generate a cache key for.\n * @returns A URL representing the main cache key for this request.\n */\n public getCacheKey(request: Request): URL {\n const key = this.getKey ? this.getKey(request) : sortSearchParams(request);\n assertKey(key);\n\n key.hash = \"\";\n return key;\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 { StatusCodes } from \"../../constants\";\nimport { HttpHeader } from \"../../constants/headers\";\nimport { GET, HEAD, Method, OPTIONS } from \"../../constants/methods\";\nimport { Time } from \"../../constants/time\";\nimport { CorsConfig } from \"../../interfaces/cors\";\n\nexport const ALLOW_ALL_ORIGINS = \"*\";\n\n/**\n * Set of HTTP methods considered \"simple\" under the `CORS` specification.\n *\n * Simple methods do not trigger a preflight request on their own.\n * (Other factors like headers can still cause a preflight.)\n */\nexport const SIMPLE_METHODS = new Set<Method>([GET, HEAD, OPTIONS]);\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 { HttpHeader } from \"../../constants/headers\";\nimport { assertMethods } from \"../../guards/methods\";\nimport { CorsConfig } from \"../../interfaces/cors\";\nimport { Worker } from \"../../interfaces/worker\";\nimport { ClonedResponse, Options } from \"../../responses\";\nimport { mergeHeader, setHeader } from \"../../utils/headers\";\nimport { ALLOW_ALL_ORIGINS, SIMPLE_METHODS, SKIP_CORS_STATUSES } from \"./constants\";\n\n/**\n * Handles a `CORS` preflight `OPTIONS` request.\n *\n * Sets the appropriate`CORS`headers based on the provided configuration\n * and the origin of the request.\n *\n * @param worker - The Worker handling the request.\n * @param cors - The `CORS` configuration.\n * @returns A Response object for the preflight request.\n */\nexport async function options(worker: Worker, cors: CorsConfig): Promise<Response> {\n const options = new Options();\n const origin = getOrigin(worker.request);\n\n if (origin) {\n setAllowOrigin(options.headers, cors, origin);\n setAllowCredentials(options.headers, cors, origin);\n }\n\n setAllowMethods(options.headers, worker);\n setAllowHeaders(options.headers, cors);\n setMaxAge(options.headers, cors);\n\n return options.response();\n}\n\n/**\n * Applies`CORS`headers to an existing response.\n *\n * Useful for normal (non-preflight) responses where the response\n * should include`CORS`headers based on the request origin.\n *\n * @param response - The original Response object.\n * @param worker - The Worker handling the request.\n * @param cors - The`CORS`configuration.\n * @returns A new Response object with`CORS`headers applied.\n */\nexport async function apply(\n response: Response,\n worker: Worker,\n cors: CorsConfig,\n): Promise<Response> {\n const clone = new ClonedResponse(response);\n const origin = getOrigin(worker.request);\n\n deleteCorsHeaders(clone.headers);\n\n if (origin) {\n setAllowOrigin(clone.headers, cors, origin);\n setAllowCredentials(clone.headers, cors, origin);\n setExposedHeaders(clone.headers, cors);\n }\n\n return clone.response();\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 } else {\n if (cors.allowedOrigins.includes(origin)) {\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_ORIGIN, origin);\n }\n mergeHeader(headers, HttpHeader.VARY, HttpHeader.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 * but only for non-simple methods.\n *\n * Simple methods (`GET`, `HEAD`, `OPTIONS`) are automatically allowed by the\n *`CORS`spec, so this function only adds methods beyond those.\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 methods = worker.getAllowedMethods();\n assertMethods(methods);\n\n const allowed = methods.filter((method) => !SIMPLE_METHODS.has(method));\n\n if (allowed.length > 0) {\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_METHODS, allowed);\n }\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 if (cors.allowedHeaders.length > 0) {\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_HEADERS, cors.allowedHeaders);\n }\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 * @param response - The Response object to inspect\n * @returns `true` if`CORS`should be skipped, `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 { CorsInit } from \"../interfaces/cors\";\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 */\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\nimport { apply, options, skipCors } from \"./utils\";\nimport { Worker } from \"../../interfaces/worker\";\nimport { CorsConfig, CorsInit } from \"../../interfaces/cors\";\nimport { defaultCorsConfig } from \"./constants\";\nimport { OPTIONS } from \"../../constants/methods\";\nimport { assertCorsInit } from \"../../guards/cors\";\nimport { Middleware } from \"../../interfaces/middleware\";\n\n/**\n * Creates 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\n/**\n * Cors Middleware Implementation\n * @see {@link cors}\n */\nclass CorsHandler implements Middleware {\n /** The configuration used for this instance, with all defaults applied. */\n private readonly config: CorsConfig;\n\n /**\n * Create a new`CORS`middleware instance.\n *\n * @param init - Partial configuration to override the defaults. Any values\n * not provided will use `defaultCorsConfig`.\n */\n constructor(init?: CorsInit) {\n this.config = { ...defaultCorsConfig, ...init };\n }\n\n /**\n * Applies`CORS`headers to a request.\n *\n * - Returns a preflight response for `OPTIONS` requests.\n * - For other methods, calls `next()` and applies`CORS`headers to the result.\n *\n * @param worker - The Worker handling the request.\n * @param next - Function to invoke the next middleware.\n * @returns Response with`CORS`headers applied.\n */\n public async handle(worker: Worker, next: () => Promise<Response>): Promise<Response> {\n if (worker.request.method === OPTIONS) {\n return options(worker, this.config);\n }\n\n const response = await next();\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 { HttpHeader } from \"../../constants/headers\";\nimport { WS_UPGRADE, WS_VERSION, WS_WEBSOCKET } from \"../../constants/websocket\";\nimport { getHeaderValues } from \"../../utils/headers\";\n\nexport function hasConnectionHeader(headers: Headers): boolean {\n return getHeaderValues(headers, HttpHeader.CONNECTION).some(\n (value) => value.toLowerCase() === WS_UPGRADE,\n );\n}\n\nexport function hasUpgradeHeader(headers: Headers): boolean {\n return getHeaderValues(headers, HttpHeader.UPGRADE).some(\n (value) => value.toLowerCase() === WS_WEBSOCKET,\n );\n}\n\nexport function hasWebSocketVersion(headers: Headers): boolean {\n return headers.get(HttpHeader.SEC_WEBSOCKET_VERSION)?.trim() === WS_VERSION;\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 { GET } from \"../../constants/methods\";\nimport { BadRequest, UpgradeRequired } from \"../../errors\";\nimport { Middleware } from \"../../interfaces/middleware\";\nimport { Worker } from \"../../interfaces/worker\";\nimport { hasConnectionHeader, hasUpgradeHeader, hasWebSocketVersion } from \"./utils\";\n\nexport function websocket(path: string = \"/\"): Middleware {\n return new WebSocketHandler(path);\n}\n\nclass WebSocketHandler implements Middleware {\n constructor(private readonly path: string) {}\n\n public handle(worker: Worker, next: () => Promise<Response>): Promise<Response> {\n if (worker.request.method !== GET) {\n return next();\n }\n\n if (this.getPath(worker.request) !== this.path) {\n return next();\n }\n\n const headers = worker.request.headers;\n if (!hasConnectionHeader(headers)) {\n return new BadRequest(\"Missing or invalid Connection header\").response();\n }\n if (!hasUpgradeHeader(headers)) {\n return new BadRequest(\"Missing or invalid Upgrade header\").response();\n }\n if (!hasWebSocketVersion(headers)) {\n return new UpgradeRequired().response();\n }\n\n return next();\n }\n\n private getPath(request: Request): string {\n return new URL(request.url).pathname;\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 { CloseCode, WS_MAX_CLOSE_CODE, WS_MAX_REASON_CHARS, WS_RESERVED_CODES } from \"../constants/websocket\";\nimport { isNumber, isString } from \"./basic\";\n\nexport function isBinary(value: unknown): value is ArrayBuffer | ArrayBufferView {\n return value instanceof ArrayBuffer || ArrayBuffer.isView(value);\n}\n\nexport function isSendable(value: unknown): value is string | ArrayBuffer | ArrayBufferView {\n if (isString(value)) return value.length > 0;\n if (isBinary(value)) return value.byteLength > 0;\n return false;\n}\n\nexport function safeCloseCode(code?: number): number {\n if (!isNumber(code)) return CloseCode.NORMAL;\n if (isCodeInRange(code) && !isReservedCode(code)) return code;\n return CloseCode.NORMAL;\n}\n\nexport function isCodeInRange(code: number): boolean {\n return code >= CloseCode.NORMAL && code <= WS_MAX_CLOSE_CODE;\n}\n\nexport function isReservedCode(code: number): boolean {\n return WS_RESERVED_CODES.has(code);\n}\n\nexport function safeReason(reason?: string): string | undefined {\n if (!isString(reason)) return;\n return reason.replaceAll(/[^\\x20-\\x7E]/g, \"\").slice(0, WS_MAX_REASON_CHARS);\n}\n\nexport function assertSerializable(value: unknown): asserts value is object {\n if (value === null || typeof value !== \"object\") {\n throw new TypeError(\"WebSocket attachment must be an object\");\n }\n try {\n JSON.stringify(value);\n } catch {\n throw new TypeError(\"WebSocket attachment is not serializable\");\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 {\n CustomEventType,\n EventOptions,\n ExtendedEventListener,\n ExtendedEventMap,\n ExtendedEventType,\n} from \"../interfaces/websocket\";\n\nexport abstract class WebSocketEvents {\n protected readonly server: WebSocket;\n\n private static isCustomEvent(type: ExtendedEventType): boolean {\n return [\"open\", \"warn\"].includes(type);\n }\n\n private customListeners: {\n [K in ExtendedEventType]?: ((ev: ExtendedEventMap[K]) => void)[];\n } = {};\n\n constructor(server: WebSocket) {\n this.server = server;\n }\n\n public addEventListener<K extends ExtendedEventType>(\n type: K,\n listener: ExtendedEventListener<K>,\n options?: EventOptions,\n ): void {\n if (WebSocketEvents.isCustomEvent(type)) {\n let arr = this.customListeners[type];\n if (!arr) {\n arr = [];\n this.customListeners[type] = arr;\n }\n arr.push(listener);\n } else {\n const finalOptions = type === \"close\" ? { ...options, once: true } : options;\n this.server.addEventListener(\n type as keyof WebSocketEventMap,\n listener as EventListener,\n finalOptions,\n );\n }\n }\n\n public removeEventListener<K extends ExtendedEventType>(\n type: K,\n listener: ExtendedEventListener<K>,\n ): void {\n if (WebSocketEvents.isCustomEvent(type)) {\n const arr = this.customListeners[type];\n if (arr) {\n const index = arr.indexOf(listener);\n if (index !== -1) arr.splice(index, 1);\n }\n } else {\n this.server.removeEventListener(\n type as keyof WebSocketEventMap,\n listener as EventListener,\n );\n }\n }\n\n private dispatch<K extends CustomEventType>(\n type: K,\n ev: ExtendedEventMap[K],\n once: boolean = false,\n ): void {\n const listeners = this.customListeners[type]?.slice() ?? [];\n if (once) {\n this.customListeners[type] = [];\n }\n for (const listener of listeners) {\n listener(ev);\n }\n }\n\n protected warn(msg: string) {\n this.dispatch(\"warn\", { type: \"warn\", message: msg });\n }\n\n protected open() {\n this.dispatch(\"open\", new Event(\"open\"), true);\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 { assertSerializable, isSendable, safeCloseCode, safeReason } from \"../guards/websocket\";\nimport { WSAttachment } from \"../interfaces/websocket\";\nimport { WebSocketEvents } from \"./events\";\n\nexport abstract class BaseWebSocket<A extends WSAttachment> extends WebSocketEvents {\n protected accepted = false;\n protected readonly server: WebSocket;\n\n constructor(server: WebSocket) {\n super(server);\n this.server = server;\n this.server.addEventListener(\"close\", this.onclose);\n }\n\n public send(data: string | ArrayBuffer | ArrayBufferView): void {\n if (this.isState(WebSocket.CONNECTING, WebSocket.CLOSED)) {\n this.warn(\"Cannot send: WebSocket not open\");\n return;\n }\n if (!isSendable(data)) {\n this.warn(\"Cannot send: empty or invalid data\");\n return;\n }\n\n this.server.send(data);\n }\n\n public get attachment(): A {\n return (this.server.deserializeAttachment() ?? {}) as A;\n }\n\n public attach(attachment?: Partial<A> | null): void {\n if (attachment === undefined) return;\n if (attachment === null) {\n this.server.serializeAttachment({});\n } else {\n const current = this.attachment;\n const merged = { ...current, ...attachment };\n assertSerializable(merged);\n this.server.serializeAttachment(merged);\n }\n }\n\n public get readyState(): number {\n if (!this.accepted) return WebSocket.CONNECTING;\n return this.server.readyState;\n }\n\n public isState(...states: number[]): boolean {\n return states.includes(this.readyState);\n }\n\n public close(code?: number, reason?: string): void {\n this.server.removeEventListener(\"close\", this.onclose);\n this.server.close(safeCloseCode(code), safeReason(reason));\n }\n\n private readonly onclose = (event: CloseEvent): void => {\n this.close(event.code, event.reason);\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 { WSAttachment, WebSocketConnection } from \"../interfaces/websocket\";\nimport { BaseWebSocket } from \"./base\";\n\nexport abstract class NewConnectionBase<A extends WSAttachment>\n extends BaseWebSocket<A>\n implements WebSocketConnection<A>\n{\n private readonly client: WebSocket;\n\n public constructor() {\n const pair = new WebSocketPair();\n const [client, server] = [pair[0], pair[1]];\n super(server);\n this.client = client;\n }\n\n public acceptWebSocket(ctx: DurableObjectState, tags?: string[]): WebSocket {\n ctx.acceptWebSocket(this.server, tags);\n return this.ready();\n }\n\n public accept(): WebSocket {\n this.server.accept();\n return this.ready();\n }\n\n private ready(): WebSocket {\n this.accepted = true;\n this.open();\n\n return this.client;\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 { WSAttachment, WebSocketConnection } from \"../interfaces/websocket\";\nimport { BaseWebSocket } from \"./base\";\n\nexport abstract class RestoredConnectionBase<A extends WSAttachment>\n extends BaseWebSocket<A>\n implements WebSocketConnection<A>\n{\n constructor(ws: WebSocket) {\n super(ws);\n this.accepted = true;\n }\n\n public accept(): WebSocket {\n throw new Error(\"Do not call accept() on restore\");\n }\n\n public acceptWebSocket(): WebSocket {\n throw new Error(\"Do not call acceptWebSocket() on restore\");\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 { WSAttachment, WebSocketConnection } from \"../interfaces/websocket\";\nimport { NewConnectionBase } from \"./new\";\nimport { RestoredConnectionBase } from \"./restore\";\n\nexport class WebSocketSessions<A extends WSAttachment = WSAttachment> {\n private readonly map = new Map<WebSocket, WebSocketConnection<A>>();\n\n public create(attachment?: Partial<A>): WebSocketConnection<A> {\n class NewConnection extends NewConnectionBase<A> {\n constructor(private readonly sessions: WebSocketSessions<A>) {\n super();\n }\n\n public override accept(): WebSocket {\n this.addEventListener(\"close\", () => this.sessions.unregister(this.server));\n this.sessions.register(this.server, this);\n return super.accept();\n }\n\n public override acceptWebSocket(ctx: DurableObjectState, tags?: string[]): WebSocket {\n this.sessions.register(this.server, this);\n return super.acceptWebSocket(ctx, tags);\n }\n }\n\n const connection = new NewConnection(this);\n connection.attach(attachment);\n return connection;\n }\n\n public restore(ws: WebSocket): WebSocketConnection<A> {\n class RestoredConnection extends RestoredConnectionBase<A> {\n constructor(sessions: WebSocketSessions<A>, restore: WebSocket) {\n super(restore);\n sessions.register(this.server, this);\n }\n }\n return new RestoredConnection(this, ws);\n }\n\n public restoreAll(all: WebSocket[]): ReadonlyArray<WebSocketConnection<A>> {\n const restored: WebSocketConnection<A>[] = [];\n for (const ws of all) {\n restored.push(this.restore(ws));\n }\n return restored;\n }\n\n public get(ws: WebSocket): WebSocketConnection<A> | undefined {\n return this.map.get(ws);\n }\n\n public values(): IterableIterator<WebSocketConnection<A>> {\n return this.map.values();\n }\n\n public keys(): IterableIterator<WebSocket> {\n return this.map.keys();\n }\n\n public close(ws: WebSocket, code?: number, reason?: string): boolean {\n const con = this.get(ws);\n if (con) con.close(code, reason);\n\n return this.unregister(ws);\n }\n\n public *[Symbol.iterator](): IterableIterator<WebSocketConnection<A>> {\n yield* this.values();\n }\n\n private register(ws: WebSocket, con: WebSocketConnection<A>): void {\n this.map.set(ws, con);\n }\n\n private unregister(ws: WebSocket): boolean {\n return this.map.delete(ws);\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 { Method } from \"../constants/methods\";\nimport { assertMethods, isMethod } from \"../guards/methods\";\nimport { FetchHandler } from \"../interfaces/fetch\";\nimport { Worker, WorkerClass } from \"../interfaces/worker\";\n\n/**\n * Provides the foundational structure for handling requests,\n * environment bindings, and the worker execution context.\n *\n * Features:\n * - Holds the current `Request` object (`request` getter).\n * - Provides access to environment bindings (`env` getter).\n * - Provides access to the worker execution context (`ctx` getter).\n * - Subclasses must implement `fetch()` to process the request.\n */\nexport abstract class BaseWorker implements Worker {\n constructor(\n private readonly _request: Request,\n private readonly _env: Env,\n private readonly _ctx: ExecutionContext,\n ) {}\n\n /** The Request object associated with this worker invocation */\n public get request(): Request {\n return this._request;\n }\n\n /** Environment bindings (e.g., KV, secrets, or other globals) */\n public get env(): Env {\n return this._env;\n }\n\n /** Execution context for background tasks or `waitUntil` */\n public get ctx(): ExecutionContext {\n return this._ctx;\n }\n\n /**\n * Dispatches the incoming request to the appropriate handler and produces a response.\n *\n * Subclasses must implement this method to define how the worker generates a `Response`\n * for the current request. This is the central point where request processing occurs.\n *\n * @returns A Promise that resolves to the `Response` for the request.\n */\n protected abstract dispatch(): Promise<Response>;\n\n /**\n * Checks if the given HTTP method is allowed for this worker.\n * @param method HTTP method string\n * @returns true if the method is allowed\n */\n public isAllowed(method: string): boolean {\n const methods = this.getAllowedMethods();\n assertMethods(methods);\n\n return isMethod(method) && methods.includes(method);\n }\n\n public abstract getAllowedMethods(): Method[];\n\n /**\n * Creates a new instance of the current Worker subclass.\n *\n * @param request - The {@link Request} to pass to the new worker instance.\n * @returns A new worker instance of the same subclass as `this`.\n */\n protected create(request: Request): this {\n const ctor = this.constructor as WorkerClass<this>;\n return new ctor(request, this.env, this.ctx);\n }\n\n /**\n * Process the {@link Request} and produce a {@link Response}.\n *\n * @returns A {@link Response} promise for the {@link Request}.\n */\n public abstract fetch(): Promise<Response>;\n\n /**\n * Simplify and standardize {@link Response} creation by extending {@link WorkerResponse}\n * or any of its subclasses and passing to this method.\n *\n * Or directly use any of the built-in classes.\n *\n * ```ts\n * this.response(TextResponse, \"Hello World!\")\n * ```\n *\n * @param ResponseClass The response class to instantiate\n * @param args Additional constructor arguments\n * @returns A Promise resolving to the {@link Response} object\n */\n protected async response<\n Ctor extends new (...args: any[]) => { response(): Promise<Response> },\n >(ResponseClass: Ctor, ...args: ConstructorParameters<Ctor>): Promise<Response> {\n return new ResponseClass(...args).response();\n }\n\n /**\n * **Ignite** your `Worker` implementation into a Cloudflare handler.\n *\n * @returns A `FetchHandler` that launches a new worker instance for each request.\n *\n * ```ts\n * export default MyWorker.ignite();\n * ```\n */\n public static ignite<W extends Worker>(this: WorkerClass<W>): FetchHandler {\n return {\n fetch: (request: Request, env: Env, ctx: ExecutionContext) =>\n new this(request, env, ctx).fetch(),\n };\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 { Middleware } from \"../interfaces/middleware\";\n\n/**\n * Asserts at runtime that a value implements the `Middleware` interface.\n *\n * @param value - The value to check.\n * @throws TypeError If `handler` does not have a `handle` method.\n */\nexport function assertMiddleware(value: unknown): asserts value is Middleware {\n if (\n value === null ||\n typeof value !== \"object\" ||\n typeof (value as Middleware).handle !== \"function\"\n ) {\n throw new TypeError(\n \"Handler must implement the Middleware interface (have a handle method).\",\n );\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 { BaseWorker } from \"./base\";\nimport { assertMiddleware } from \"../guards/middleware\";\nimport { Middleware } from \"../interfaces/middleware\";\n\n/** Base worker for handling middleware chains. */\nexport abstract class MiddlewareWorker extends BaseWorker {\n /** Middleware handlers registered for this worker. */\n protected readonly middlewares: Middleware[] = [];\n\n /**\n * Hook for subclasses to perform any initialization.\n */\n protected init(): void | Promise<void> {\n return;\n }\n\n /**\n * Add one or more middleware instances to this worker.\n *\n * The middleware will run for every request handled by this worker,\n * in the order they are added.\n *\n * @param middleware - One or more middleware instances to run.\n * @returns `this` to allow chaining multiple `.use()` calls.\n */\n public use(...middleware: Middleware[]): this {\n middleware.forEach(assertMiddleware);\n\n this.middlewares.push(...middleware);\n return this;\n }\n\n /**\n * Executes the middleware chain and dispatches the request.\n *\n * @returns The Response produced by the last middleware or `dispatch()`.\n */\n public override async fetch(): Promise<Response> {\n const chain = this.middlewares.reduceRight(\n (next, handler) => () => handler.handle(this, next),\n () => this.dispatch(),\n );\n return await chain();\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 { MethodNotAllowed, InternalServerError, MethodNotImplemented } from \"../errors\";\nimport { MiddlewareWorker } from \"./middleware\";\nimport { Head, Options } from \"../responses\";\nimport { Method, GET, HEAD, OPTIONS } from \"../constants/methods\";\n\n/**\n * Basic worker class providing HTTP method dispatching and error handling.\n */\nexport abstract class BasicWorker extends MiddlewareWorker {\n /**\n * Entry point to handle a fetch request.\n */\n public override async fetch(): Promise<Response> {\n if (!this.isAllowed(this.request.method)) {\n return this.response(MethodNotAllowed, this);\n }\n\n try {\n await this.init();\n return await super.fetch();\n } catch (error) {\n console.error(error);\n return this.response(InternalServerError);\n }\n }\n\n /**\n * Dispatches the request to the method-specific handler.\n */\n protected override async dispatch(): Promise<Response> {\n const method = this.request.method as Method;\n const handler: Record<Method, () => Promise<Response>> = {\n GET: () => this.get(),\n PUT: () => this.put(),\n HEAD: () => this.head(),\n POST: () => this.post(),\n PATCH: () => this.patch(),\n DELETE: () => this.delete(),\n OPTIONS: () => this.options(),\n };\n\n return (handler[method] ?? (() => this.response(MethodNotAllowed, this)))();\n }\n\n /** Override and implement this method for `GET` requests. */\n protected async get(): Promise<Response> {\n return this.response(MethodNotImplemented, this);\n }\n\n /** Override and implement this method for `PUT` requests. */\n protected async put(): Promise<Response> {\n return this.response(MethodNotImplemented, this);\n }\n\n /** Override and implement this method for `POST` requests. */\n protected async post(): Promise<Response> {\n return this.response(MethodNotImplemented, this);\n }\n\n /** Override and implement this method for `PATCH` requests. */\n protected async patch(): Promise<Response> {\n return this.response(MethodNotImplemented, this);\n }\n\n /** Override and implement this method for `DELETE` requests. */\n protected async delete(): Promise<Response> {\n return this.response(MethodNotImplemented, this);\n }\n\n /** Returns a default empty `OPTIONS` response. */\n protected async options(): Promise<Response> {\n return this.response(Options);\n }\n\n /**\n * Default handler for `HEAD` requests.\n * Performs a `GET` request and removes the body for `HEAD` semantics.\n *\n * Usually does not need to be overridden as this behavior covers\n * standard `HEAD` requirements.\n */\n protected async head(): Promise<Response> {\n const worker = this.create(\n new Request(this.request.url, { method: GET, headers: this.request.headers }),\n );\n return this.response(Head, await worker.fetch());\n }\n\n /**\n * The DEFAULT allowed HTTP methods for subclasses are `GET`, `HEAD`, `OPTIONS`.\n *\n * These defaults were selected for getting started quickly and can be\n * overridden for each worker subclass.\n */\n public getAllowedMethods(): Method[] {\n return [GET, HEAD, OPTIONS];\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 { match } from \"path-to-regexp\";\nimport { MatchedRoute, Route, PathParams, RouteTable } from \"./interfaces/route\";\nimport { Method } from \"./constants/methods\";\n\n/**\n * Container for route definitions and matching logic.\n * Implements Iterable to allow iteration over all routes.\n */\nexport class Routes implements Iterable<Route> {\n /** Internal array of registered routes */\n private readonly routes: Route[] = [];\n\n /**\n * Add routes to the router.\n *\n * Accepts any iterable of [method, path, handler] tuples.\n * This includes arrays, Sets, or generators.\n *\n * @param routes - Iterable of route tuples to add.\n */\n public add(routes: RouteTable): void {\n for (const [method, path, handler] of routes) {\n const matcher = match<PathParams>(path);\n this.routes.push({ method, matcher, handler });\n }\n }\n\n /**\n * Attempt to match a URL against the registered routes.\n *\n * @param method - HTTP method of the request\n * @param url - Full URL string to match against\n * @returns A MatchedRoute object if a route matches, otherwise null\n */\n public match(method: Method, url: string): MatchedRoute | null {\n const pathname = new URL(url).pathname;\n\n for (const route of this) {\n if (route.method !== method) continue;\n\n const found = route.matcher(pathname);\n if (found) return { route, params: found.params };\n }\n\n return null;\n }\n\n /**\n * Iterate over all registered routes.\n */\n public *[Symbol.iterator](): Iterator<Route> {\n yield* this.routes;\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 { BasicWorker } from \"./basic\";\nimport { NotFound } from \"../errors\";\nimport { Routes } from \"../routes\";\nimport { RouteHandler, RouteTable } from \"../interfaces/route\";\nimport { WorkerClass } from \"../interfaces/worker\";\nimport { BaseWorker } from \"./base\";\nimport { Method } from \"../constants/methods\";\n\n/**\n * Base worker supporting route-based request handling.\n *\n * Subclass `RouteWorker` to define a worker with multiple route handlers.\n *\n * Routes can be registered individually via `route()` or in bulk via `routes()`.\n */\nexport abstract class RouteWorker extends BasicWorker {\n /** Internal table of registered routes. */\n private readonly _routes: Routes = new Routes();\n\n /**\n * Registers a single new route in the worker.\n *\n * When a request matches the specified method and path, the provided handler\n * will be executed. The handler can be either:\n * - A function that receives URL parameters, or\n * - A Worker subclass that will handle the request.\n *\n * @param method - HTTP method for the route (`GET`, `POST`, etc.).\n * @param path - URL path pattern (Express-style, e.g., \"/users/:id\").\n * @param handler - The function or Worker class to run when the route matches.\n * @returns The current worker instance, allowing method chaining.\n */\n protected route(method: Method, path: string, handler: RouteHandler): this {\n this.routes([[method, path, handler]]);\n return this;\n }\n\n /**\n * Registers multiple routes at once in the worker.\n *\n * Each route should be a tuple `[method, path, handler]` where:\n * - `method` - HTTP method for the route (`GET`, `POST`, etc.).\n * - `path` - URL path pattern (Express-style, e.g., \"/users/:id\").\n * - `handler` - A function that receives URL parameters or a Worker subclass\n * that will handle the request.\n *\n * @param routes - An iterable of routes to register. Each item is a `[method, path, handler]` tuple.\n * @returns The current worker instance, allowing method chaining.\n */\n protected routes(routes: RouteTable): this {\n this._routes.add(routes);\n return this;\n }\n\n /**\n * Matches the incoming request against registered routes and dispatches it.\n *\n * If a route is found:\n * - If the handler is a Worker class, a new instance is created and its `fetch()` is called.\n * - If the handler is a callback function, it is invoked with the extracted path parameters.\n *\n * If no route matches, the request is passed to the parent `dispatch()` handler.\n *\n * @returns A `Promise<Response>` from the matched handler or parent dispatch.\n */\n protected override async dispatch(): Promise<Response> {\n const found = this._routes.match(this.request.method as Method, this.request.url);\n if (!found) return super.dispatch();\n\n const { handler } = found.route;\n if (RouteWorker.isWorkerClass(handler)) {\n return new handler(this.request, this.env, this.ctx).fetch();\n }\n return handler.call(this, found.params);\n }\n\n /**\n * Runtime type guard to check if a given handler is a Worker class.\n *\n * A Worker class is any class that extends `BaseWorker`.\n *\n * @param handler - The constructor function to test.\n * @returns `true` if `handler` is a subclass of `BaseWorker` at runtime, `false` otherwise.\n */\n private static isWorkerClass(handler: RouteHandler): handler is WorkerClass {\n return BaseWorker.prototype.isPrototypeOf(handler.prototype);\n }\n\n protected override async get(): Promise<Response> {\n return this.response(NotFound);\n }\n\n protected override async put(): Promise<Response> {\n return this.response(NotFound);\n }\n\n protected override async post(): Promise<Response> {\n return this.response(NotFound);\n }\n\n protected override async patch(): Promise<Response> {\n return this.response(NotFound);\n }\n\n protected override async delete(): Promise<Response> {\n return this.response(NotFound);\n }\n}\n"],"mappings":"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","names":["StatusCodes","CacheLib","CacheControl","Method","GET","PUT","HEAD","POST","PATCH","DELETE","OPTIONS","Time","isStringArray","value","item","isString","isFunction","isNumber","isBoolean","assertCacheName","value","isString","assertGetKey","isFunction","assertKey","StatusCodes","HttpHeader","FORBIDDEN_304_HEADERS","FORBIDDEN_204_HEADERS","lexCompare","a","b","setHeader","headers","key","value","raw","values","v","lexCompare","mergeHeader","merged","getHeaderValues","filterHeaders","keys","VARY_CACHE_URL","isCacheable","request","response","GET","StatusCodes","getVaryHeader","values","getHeaderValues","HttpHeader","v","lexCompare","filterVaryHeader","vary","h","value","getVaryKey","key","varyPairs","filtered","header","encoded","base64UrlEncode","str","utf8","binary","byte","StatusCodes","CachePolicy","rules","worker","getCached","next","rule","response","GetMethodRule","worker","next","GET","RANGE_REGEX","getRange","request","range","match","start","end","normalizeWeak","etag","normalizeStrong","getCacheControl","headers","CacheControl","HttpHeader","getCacheValidators","getHeaderValues","hasCacheValidator","ifNoneMatch","ifMatch","ifModifiedSince","RangeRule","worker","next","range","getRange","response","lengthHeader","HttpHeader","length","isNumber","getReasonPhrase","StatusCodes","getReasonPhrase","StatusCodes","UTF8_CHARSET","withCharset","mediaType","charset","assertOctetStreamInit","value","obj","size","isNumber","offset","length","BaseResponse","StatusCodes","withCharset","UTF8_CHARSET","getReasonPhrase","key","value","setHeader","mergeHeader","HttpHeader","filterHeaders","FORBIDDEN_204_HEADERS","FORBIDDEN_304_HEADERS","CacheResponse","cache","CacheControl","WorkerResponse","body","ClonedResponse","response","clone","NotModified","SuccessResponse","status","JsonResponse","json","HtmlResponse","charset","TextResponse","OctetStream","_OctetStream","stream","init","assertOctetStreamInit","normalized","size","offset","length","R2ObjectStream","_R2ObjectStream","source","useCache","range","WebSocketUpgrade","client","Head","get","Options","METHOD_SET","Method","isMethod","value","isString","isMethodArray","assertMethods","desc","WS_UPGRADE","WS_WEBSOCKET","CloseCode","WS_RESERVED_CODES","HttpError","JsonResponse","status","details","json","getReasonPhrase","CacheControl","BadRequest","StatusCodes","Unauthorized","Forbidden","NotFound","MethodNotAllowed","worker","methods","assertMethods","HttpHeader","PreconditionFailed","WorkerResponse","UpgradeRequired","InternalServerError","NotImplemented","MethodNotImplemented","ServiceUnavailable","ETagRule","worker","next","response","etag","HttpHeader","ifNoneMatch","ifMatch","getCacheValidators","normalizedEtag","normalizeStrong","PreconditionFailed","normalizeWeak","normalizedMatches","NotModified","LastModifiedRule","worker","next","response","lastModified","HttpHeader","ifModifiedSince","getCacheValidators","lastModifiedTime","ifModifiedSinceTime","NotModified","CacheControlRule","worker","next","cache","getCacheControl","hasCacheValidator","sortSearchParams","request","url","sorted","a","b","lexCompare","stripSearchParams","cache","cacheName","getKey","assertCacheName","assertGetKey","CacheHandler","worker","next","cacheResponse","CachePolicy","GetMethodRule","CacheControlRule","RangeRule","ETagRule","LastModifiedRule","response","request","url","vary","key","getVaryKey","isCacheable","filterVaryHeader","getVaryHeader","sortSearchParams","assertKey","ALLOW_ALL_ORIGINS","SIMPLE_METHODS","GET","HEAD","OPTIONS","SKIP_CORS_STATUSES","StatusCodes","defaultCorsConfig","HttpHeader","Time","options","worker","cors","Options","origin","getOrigin","setAllowOrigin","setAllowCredentials","setAllowMethods","setAllowHeaders","setMaxAge","apply","response","clone","ClonedResponse","deleteCorsHeaders","setExposedHeaders","headers","allowAllOrigins","setHeader","HttpHeader","ALLOW_ALL_ORIGINS","mergeHeader","methods","assertMethods","allowed","method","SIMPLE_METHODS","maxAge","skipCors","status","SKIP_CORS_STATUSES","request","assertCorsInit","value","obj","isStringArray","isBoolean","isNumber","cors","init","assertCorsInit","CorsHandler","defaultCorsConfig","worker","next","OPTIONS","options","response","skipCors","apply","hasConnectionHeader","headers","getHeaderValues","HttpHeader","value","WS_UPGRADE","hasUpgradeHeader","WS_WEBSOCKET","hasWebSocketVersion","websocket","path","WebSocketHandler","worker","next","GET","headers","hasConnectionHeader","hasUpgradeHeader","hasWebSocketVersion","UpgradeRequired","BadRequest","request","isBinary","value","isSendable","isString","safeCloseCode","code","isNumber","isCodeInRange","isReservedCode","CloseCode","WS_RESERVED_CODES","safeReason","reason","assertSerializable","WebSocketEvents","_WebSocketEvents","type","server","listener","options","arr","finalOptions","index","ev","once","listeners","msg","BaseWebSocket","WebSocketEvents","server","data","isSendable","attachment","merged","assertSerializable","states","code","reason","safeCloseCode","safeReason","event","NewConnectionBase","BaseWebSocket","pair","client","server","ctx","tags","RestoredConnectionBase","BaseWebSocket","ws","WebSocketSessions","attachment","NewConnection","NewConnectionBase","sessions","ctx","tags","connection","ws","RestoredConnection","RestoredConnectionBase","restore","all","restored","code","reason","con","BaseWorker","_request","_env","_ctx","method","methods","assertMethods","isMethod","request","ctor","ResponseClass","args","env","ctx","assertMiddleware","value","MiddlewareWorker","BaseWorker","middleware","assertMiddleware","next","handler","BasicWorker","MiddlewareWorker","MethodNotAllowed","error","InternalServerError","method","MethodNotImplemented","Options","worker","GET","Head","HEAD","OPTIONS","match","Routes","routes","method","path","handler","matcher","url","pathname","route","found","RouteWorker","_RouteWorker","BasicWorker","Routes","method","path","handler","routes","found","BaseWorker","NotFound"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonix.org/cloud-spark",
3
- "version": "0.0.167",
3
+ "version": "0.0.169",
4
4
  "description": "Ignite your Cloudflare Workers with a type-safe library for rapid development.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -53,7 +53,7 @@
53
53
  "tsup": "^8.5.0",
54
54
  "typescript": "^5.9.2",
55
55
  "vitest": "^3.2.4",
56
- "wrangler": "^4.40.3"
56
+ "wrangler": "^4.41.0"
57
57
  },
58
58
  "dependencies": {
59
59
  "cache-control-parser": "^2.0.6",