@adonix.org/cloud-spark 0.0.89 → 0.0.91

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
@@ -79,7 +79,7 @@ declare abstract class BaseWorker implements Worker {
79
79
  }
80
80
 
81
81
  /**
82
- * - See https://github.com/etienne-martin/cache-control-parser
82
+ * @see {@link https://github.com/etienne-martin/cache-control-parser | cache-control-parser}
83
83
  */
84
84
  type CacheControl = CacheLib.CacheControl;
85
85
  declare const CacheControl: {
@@ -225,15 +225,6 @@ declare function mergeHeader(headers: Headers, key: string, value: string | stri
225
225
  * @returns A normalized URL string suitable for hashing or direct cache key use.
226
226
  */
227
227
  declare function normalizeUrl(url: string): URL;
228
- /**
229
- * Encodes a given string into Base64 using UTF-8 encoding.
230
- *
231
- * This function correctly handles any Unicode characters.
232
- *
233
- * @param raw - The input string to encode.
234
- * @returns The Base64-encoded string.
235
- */
236
- declare function toBase64(raw: string): string;
237
228
 
238
229
  /**
239
230
  * Provides information about the CORS policy for the current request.
@@ -280,30 +271,6 @@ declare namespace Cors {
280
271
  * @param headers The Headers object to update
281
272
  */
282
273
  declare function addCorsHeaders(cors: CorsProvider, headers: Headers): void;
283
- /**
284
- * Deletes all standard CORS headers from the given Headers object.
285
- * Useful for cleaning cached responses or resetting headers before reapplying CORS.
286
- *
287
- * @param headers The Headers object to clean
288
- */
289
- declare function deleteCorsHeaders(headers: Headers): void;
290
- /**
291
- * Returns a new Response with CORS headers applied according to the policy.
292
- * Original response is not mutated.
293
- *
294
- * @param cors The CorsProvider instance for policy
295
- * @param res The original Response object
296
- * @returns A new Response with updated CORS headers
297
- */
298
- declare function withCorsHeaders(cors: CorsProvider, res: Response): Response;
299
- /**
300
- * Returns a new Response with all standard CORS headers removed.
301
- * Useful for storing responses in a cache without per-request CORS headers.
302
- *
303
- * @param res The original Response object
304
- * @returns A new Response without CORS headers
305
- */
306
- declare function withoutCorsHeaders(res: Response): Response;
307
274
 
308
275
  /**
309
276
  * Abstract base class for Workers to provide a default CORS policy.
@@ -328,33 +295,43 @@ declare abstract class CorsWorker extends BaseWorker implements CorsProvider {
328
295
  }
329
296
 
330
297
  /**
331
- * Abstract base class for Workers that support caching of GET responses.
298
+ * Abstract worker class that adds caching support for GET requests.
332
299
  *
333
- * Features:
334
- * - URL-based caching using `Request.url`.
335
- * - Removes per-request CORS headers before storing in cache.
336
- * - Dynamically applies correct CORS headers when retrieving cached responses.
337
- * - Only caches successful GET requests.
300
+ * Behavior:
301
+ * - Caches successful GET responses (`response.ok === true`) in the selected cache.
302
+ * - Strips CORS headers from cached responses; all other origin headers are preserved.
303
+ * - Dynamically adds CORS headers to cached responses when returned to the client.
304
+ *
305
+ * Subclasses should override `getCacheKey()` to customize cache key generation if needed.
338
306
  */
339
307
  declare abstract class CacheWorker extends CorsWorker {
340
308
  /**
341
309
  * Returns the cache key for the current request.
342
- * By default, this is the normalized request URL.
343
310
  *
344
- * @returns A URL or RequestInfo to use as the cache key
311
+ * Behavior:
312
+ * - By default, returns the normalized request URL.
313
+ * - Query parameters are normalized so that the order does not affect the cache key.
314
+ * For example, `?a=1&b=2` and `?b=2&a=1` produce the same cache key.
315
+ *
316
+ * Subclasses may override this method to implement custom cache key strategies.
317
+ *
318
+ * @returns {URL | RequestInfo} The URL or RequestInfo used as the cache key.
345
319
  */
346
320
  protected getCacheKey(): URL | RequestInfo;
347
321
  /**
348
322
  * Retrieves a cached Response for the current request, if one exists.
349
323
  *
350
324
  * Behavior:
351
- * - Only GET requests are cached.
352
- * - If a cached response is found, CORS headers are applied dynamically
353
- * using `withCorsHeaders` before returning.
354
- * - Returns undefined if no cached response is found or if request is not GET.
325
+ * - Only GET requests are considered.
326
+ * - Returns a new Response with dynamic CORS headers applied via `addCacheHeaders`.
327
+ * - Returns `undefined` if no cached response is found.
328
+ * - Cloudflare dynamic headers (`CF-Cache-Status`, `Age`, `Connection`, etc.) will
329
+ * always be present on the returned response, even though they are not stored in the cache.
355
330
  *
356
- * @param cacheName Optional name of the cache to use; defaults to `caches.default`.
357
- * @returns A Promise resolving to a Response with correct CORS headers, or undefined.
331
+ * @param {string} [cacheName] Optional named cache; defaults to `caches.default`.
332
+ * @returns {Promise<Response | undefined>} A Response with CORS headers, or undefined.
333
+ * @see {@link setCachedResponse}
334
+ * @see {@link getCacheKey}
358
335
  */
359
336
  protected getCachedResponse(cacheName?: string): Promise<Response | undefined>;
360
337
  /**
@@ -362,13 +339,40 @@ declare abstract class CacheWorker extends CorsWorker {
362
339
  *
363
340
  * Behavior:
364
341
  * - Only caches successful GET responses (`response.ok === true`).
365
- * - Removes all CORS headers before storing using `withoutCorsHeaders`.
366
- * - Uses `ctx.waitUntil` to store asynchronously without blocking the worker.
342
+ * - Strips headers via `removeCacheHeaders` before storing.
343
+ * - Uses `ctx.waitUntil` to perform caching asynchronously without blocking the response.
344
+ * - All other origin headers (e.g., Cache-Control, Expires) are preserved.
367
345
  *
368
- * @param response The Response to cache
369
- * @param cacheName Optional name of the cache to use; defaults to `caches.default`.
346
+ * @param {Response} response The Response to cache.
347
+ * @param {string} [cacheName] Optional named cache; defaults to `caches.default`.
348
+ * @see {@link getCachedResponse}
349
+ * @see {@link getCacheKey}
370
350
  */
371
351
  protected setCachedResponse(response: Response, cacheName?: string): Promise<void>;
352
+ /**
353
+ * Adds headers to a cached response.
354
+ *
355
+ * @param {Response} cached The cached Response.
356
+ * @returns {Response} A new Response with dynamic CORS headers applied.
357
+ * @see {@link removeCacheHeaders}
358
+ */
359
+ private addCacheHeaders;
360
+ /**
361
+ * Removes headers that should not be stored in the cache (currently only CORS headers).
362
+ *
363
+ * @param {Response} response The Response to clean before caching.
364
+ * @returns {Response} A new Response with excluded headers removed.
365
+ * @see {@link addCacheHeaders}
366
+ */
367
+ private removeCacheHeaders;
368
+ /**
369
+ * Returns the list of headers to exclude from the cached response.
370
+ * By default, excludes only dynamic CORS headers.
371
+ *
372
+ * @returns {string[]} Array of header names to exclude.
373
+ * @see {@link removeCacheHeaders}
374
+ */
375
+ protected excludeCacheHeaders(): string[];
372
376
  }
373
377
 
374
378
  interface ErrorJson {
@@ -396,7 +400,7 @@ declare abstract class CorsResponse extends BaseResponse {
396
400
  declare abstract class CacheResponse extends CorsResponse {
397
401
  cache?: CacheControl | undefined;
398
402
  constructor(cors: CorsProvider, body?: BodyInit | null, cache?: CacheControl | undefined);
399
- protected addCacheHeader(): void;
403
+ protected addCacheHeaders(): void;
400
404
  }
401
405
  declare abstract class WorkerResponse extends CacheResponse {
402
406
  createResponse(): Response;
@@ -454,7 +458,10 @@ declare class InternalServerError extends HttpError {
454
458
  constructor(cors: CorsProvider, details?: string);
455
459
  }
456
460
  declare class NotImplemented extends HttpError {
457
- constructor(cors: CorsProvider);
461
+ constructor(cors: CorsProvider, details?: string);
462
+ }
463
+ declare class MethodNotImplemented extends NotImplemented {
464
+ constructor(cors: CorsProvider, method: Method);
458
465
  }
459
466
  declare class ServiceUnavailable extends HttpError {
460
467
  constructor(cors: CorsProvider, details?: string);
@@ -536,4 +543,4 @@ declare abstract class RoutedWorker extends BasicWorker {
536
543
  protected delete(): Promise<Response>;
537
544
  }
538
545
 
539
- export { BadRequest, BasicWorker, CacheControl, ClonedResponse, Cors, type CorsProvider, type ErrorJson, Forbidden, Head, HtmlResponse, HttpError, HttpHeader, InternalServerError, JsonResponse, MediaType, Method, MethodNotAllowed, NotFound, NotImplemented, Options, Route, type RouteCallback, type RouteInit, RoutedWorker, Routes, ServiceUnavailable, SuccessResponse, TextResponse, Time, Unauthorized, type Worker, WorkerResponse, addCorsHeaders, deleteCorsHeaders, getContentType, isMethod, mergeHeader, normalizeUrl, setHeader, toBase64, withCorsHeaders, withoutCorsHeaders };
546
+ export { BadRequest, BasicWorker, CacheControl, ClonedResponse, Cors, type CorsProvider, type ErrorJson, Forbidden, Head, HtmlResponse, HttpError, HttpHeader, InternalServerError, JsonResponse, MediaType, Method, MethodNotAllowed, MethodNotImplemented, NotFound, NotImplemented, Options, Route, type RouteCallback, type RouteInit, RoutedWorker, Routes, ServiceUnavailable, SuccessResponse, TextResponse, Time, Unauthorized, type Worker, WorkerResponse, addCorsHeaders, getContentType, isMethod, mergeHeader, normalizeUrl, setHeader };
package/dist/index.js CHANGED
@@ -142,14 +142,6 @@ function normalizeUrl(url) {
142
142
  u.hash = "";
143
143
  return u;
144
144
  }
145
- function toBase64(raw) {
146
- const bytes = new TextEncoder().encode(raw);
147
- let binary = "";
148
- for (const b of bytes) {
149
- binary += String.fromCharCode(b);
150
- }
151
- return btoa(binary);
152
- }
153
145
 
154
146
  // src/cors.ts
155
147
  var Cors;
@@ -185,24 +177,6 @@ function deleteCorsHeaders(headers) {
185
177
  headers.delete(Cors.ALLOW_METHODS);
186
178
  headers.delete(Cors.MAX_AGE);
187
179
  }
188
- function withCorsHeaders(cors, res) {
189
- const headers = new Headers(res.headers);
190
- addCorsHeaders(cors, headers);
191
- return new Response(res.body, {
192
- status: res.status,
193
- statusText: res.statusText,
194
- headers
195
- });
196
- }
197
- function withoutCorsHeaders(res) {
198
- const headers = new Headers(res.headers);
199
- deleteCorsHeaders(headers);
200
- return new Response(res.body, {
201
- status: res.status,
202
- statusText: res.statusText,
203
- headers
204
- });
205
- }
206
180
 
207
181
  // src/base-worker.ts
208
182
  var BaseWorker = class {
@@ -269,9 +243,15 @@ var CorsWorker = class extends BaseWorker {
269
243
  var CacheWorker = class extends CorsWorker {
270
244
  /**
271
245
  * Returns the cache key for the current request.
272
- * By default, this is the normalized request URL.
273
246
  *
274
- * @returns A URL or RequestInfo to use as the cache key
247
+ * Behavior:
248
+ * - By default, returns the normalized request URL.
249
+ * - Query parameters are normalized so that the order does not affect the cache key.
250
+ * For example, `?a=1&b=2` and `?b=2&a=1` produce the same cache key.
251
+ *
252
+ * Subclasses may override this method to implement custom cache key strategies.
253
+ *
254
+ * @returns {URL | RequestInfo} The URL or RequestInfo used as the cache key.
275
255
  */
276
256
  getCacheKey() {
277
257
  return normalizeUrl(this.request.url);
@@ -280,36 +260,97 @@ var CacheWorker = class extends CorsWorker {
280
260
  * Retrieves a cached Response for the current request, if one exists.
281
261
  *
282
262
  * Behavior:
283
- * - Only GET requests are cached.
284
- * - If a cached response is found, CORS headers are applied dynamically
285
- * using `withCorsHeaders` before returning.
286
- * - Returns undefined if no cached response is found or if request is not GET.
263
+ * - Only GET requests are considered.
264
+ * - Returns a new Response with dynamic CORS headers applied via `addCacheHeaders`.
265
+ * - Returns `undefined` if no cached response is found.
266
+ * - Cloudflare dynamic headers (`CF-Cache-Status`, `Age`, `Connection`, etc.) will
267
+ * always be present on the returned response, even though they are not stored in the cache.
287
268
  *
288
- * @param cacheName Optional name of the cache to use; defaults to `caches.default`.
289
- * @returns A Promise resolving to a Response with correct CORS headers, or undefined.
269
+ * @param {string} [cacheName] Optional named cache; defaults to `caches.default`.
270
+ * @returns {Promise<Response | undefined>} A Response with CORS headers, or undefined.
271
+ * @see {@link setCachedResponse}
272
+ * @see {@link getCacheKey}
290
273
  */
291
274
  async getCachedResponse(cacheName) {
292
275
  if (this.request.method !== "GET" /* GET */) return;
293
276
  const cache = cacheName ? await caches.open(cacheName) : caches.default;
294
277
  const response = await cache.match(this.getCacheKey());
295
- return response ? withCorsHeaders(this, response) : void 0;
278
+ return response ? this.addCacheHeaders(response) : void 0;
296
279
  }
297
280
  /**
298
281
  * Stores a Response in the cache for the current request.
299
282
  *
300
283
  * Behavior:
301
284
  * - Only caches successful GET responses (`response.ok === true`).
302
- * - Removes all CORS headers before storing using `withoutCorsHeaders`.
303
- * - Uses `ctx.waitUntil` to store asynchronously without blocking the worker.
285
+ * - Strips headers via `removeCacheHeaders` before storing.
286
+ * - Uses `ctx.waitUntil` to perform caching asynchronously without blocking the response.
287
+ * - All other origin headers (e.g., Cache-Control, Expires) are preserved.
304
288
  *
305
- * @param response The Response to cache
306
- * @param cacheName Optional name of the cache to use; defaults to `caches.default`.
289
+ * @param {Response} response The Response to cache.
290
+ * @param {string} [cacheName] Optional named cache; defaults to `caches.default`.
291
+ * @see {@link getCachedResponse}
292
+ * @see {@link getCacheKey}
307
293
  */
308
294
  async setCachedResponse(response, cacheName) {
309
295
  if (!response.ok) return;
310
296
  if (this.request.method !== "GET" /* GET */) return;
311
297
  const cache = cacheName ? await caches.open(cacheName) : caches.default;
312
- this.ctx?.waitUntil(cache.put(this.getCacheKey(), withoutCorsHeaders(response.clone())));
298
+ this.ctx?.waitUntil(
299
+ cache.put(this.getCacheKey(), this.removeCacheHeaders(response.clone()))
300
+ );
301
+ }
302
+ /**
303
+ * Adds headers to a cached response.
304
+ *
305
+ * @param {Response} cached The cached Response.
306
+ * @returns {Response} A new Response with dynamic CORS headers applied.
307
+ * @see {@link removeCacheHeaders}
308
+ */
309
+ addCacheHeaders(cached) {
310
+ const headers = new Headers(cached.headers);
311
+ addCorsHeaders(this, headers);
312
+ return new Response(cached.body, {
313
+ status: cached.status,
314
+ statusText: cached.statusText,
315
+ headers
316
+ });
317
+ }
318
+ /**
319
+ * Removes headers that should not be stored in the cache (currently only CORS headers).
320
+ *
321
+ * @param {Response} response The Response to clean before caching.
322
+ * @returns {Response} A new Response with excluded headers removed.
323
+ * @see {@link addCacheHeaders}
324
+ */
325
+ removeCacheHeaders(response) {
326
+ const excludeSet = new Set(this.excludeCacheHeaders().map((h) => h.toLowerCase()));
327
+ const headers = new Headers();
328
+ for (const [key, value] of response.headers) {
329
+ if (!excludeSet.has(key)) {
330
+ headers.set(key, value);
331
+ }
332
+ }
333
+ return new Response(response.body, {
334
+ status: response.status,
335
+ statusText: response.statusText,
336
+ headers
337
+ });
338
+ }
339
+ /**
340
+ * Returns the list of headers to exclude from the cached response.
341
+ * By default, excludes only dynamic CORS headers.
342
+ *
343
+ * @returns {string[]} Array of header names to exclude.
344
+ * @see {@link removeCacheHeaders}
345
+ */
346
+ excludeCacheHeaders() {
347
+ return [
348
+ Cors.ALLOW_ORIGIN,
349
+ Cors.ALLOW_CREDENTIALS,
350
+ Cors.EXPOSE_HEADERS,
351
+ Cors.ALLOW_METHODS,
352
+ Cors.MAX_AGE
353
+ ];
313
354
  }
314
355
  };
315
356
 
@@ -357,7 +398,7 @@ var CacheResponse = class extends CorsResponse {
357
398
  super(cors, body);
358
399
  this.cache = cache;
359
400
  }
360
- addCacheHeader() {
401
+ addCacheHeaders() {
361
402
  if (this.cache) {
362
403
  this.headers.set(HttpHeader.CACHE_CONTROL, CacheControl.stringify(this.cache));
363
404
  }
@@ -366,7 +407,7 @@ var CacheResponse = class extends CorsResponse {
366
407
  var WorkerResponse = class extends CacheResponse {
367
408
  createResponse() {
368
409
  this.addCorsHeaders();
369
- this.addCacheHeader();
410
+ this.addCacheHeaders();
370
411
  this.addSecurityHeaders();
371
412
  const body = this.status === StatusCodes2.NO_CONTENT ? null : this.body;
372
413
  if (body) this.addContentType();
@@ -482,8 +523,13 @@ var InternalServerError = class extends HttpError {
482
523
  }
483
524
  };
484
525
  var NotImplemented = class extends HttpError {
485
- constructor(cors) {
486
- super(cors, StatusCodes2.NOT_IMPLEMENTED);
526
+ constructor(cors, details) {
527
+ super(cors, StatusCodes2.NOT_IMPLEMENTED, details);
528
+ }
529
+ };
530
+ var MethodNotImplemented = class extends NotImplemented {
531
+ constructor(cors, method) {
532
+ super(cors, `${method} method not implemented.`);
487
533
  }
488
534
  };
489
535
  var ServiceUnavailable = class extends HttpError {
@@ -518,19 +564,19 @@ var BasicWorker = class extends CacheWorker {
518
564
  return (handler[method] ?? (() => this.getResponse(MethodNotAllowed, method)))();
519
565
  }
520
566
  async get() {
521
- return this.getResponse(NotImplemented);
567
+ return this.getResponse(MethodNotImplemented, "GET" /* GET */);
522
568
  }
523
569
  async put() {
524
- return this.getResponse(NotImplemented);
570
+ return this.getResponse(MethodNotImplemented, "PUT" /* PUT */);
525
571
  }
526
572
  async post() {
527
- return this.getResponse(NotImplemented);
573
+ return this.getResponse(MethodNotImplemented, "POST" /* POST */);
528
574
  }
529
575
  async patch() {
530
- return this.getResponse(NotImplemented);
576
+ return this.getResponse(MethodNotImplemented, "PATCH" /* PATCH */);
531
577
  }
532
578
  async delete() {
533
- return this.getResponse(NotImplemented);
579
+ return this.getResponse(MethodNotImplemented, "DELETE" /* DELETE */);
534
580
  }
535
581
  async options() {
536
582
  return this.getResponse(Options);
@@ -646,6 +692,7 @@ export {
646
692
  MediaType,
647
693
  Method,
648
694
  MethodNotAllowed,
695
+ MethodNotImplemented,
649
696
  NotFound,
650
697
  NotImplemented,
651
698
  Options,
@@ -660,14 +707,10 @@ export {
660
707
  Unauthorized,
661
708
  WorkerResponse,
662
709
  addCorsHeaders,
663
- deleteCorsHeaders,
664
710
  getContentType,
665
711
  isMethod,
666
712
  mergeHeader,
667
713
  normalizeUrl,
668
- setHeader,
669
- toBase64,
670
- withCorsHeaders,
671
- withoutCorsHeaders
714
+ setHeader
672
715
  };
673
716
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/common.ts","../src/cors.ts","../src/base-worker.ts","../src/cors-worker.ts","../src/cache-worker.ts","../src/response.ts","../src/basic-worker.ts","../src/routes.ts","../src/routed-worker.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\nimport CacheLib from \"cache-control-parser\";\n\n/**\n * - See https://github.com/etienne-martin/cache-control-parser\n */\nexport type CacheControl = CacheLib.CacheControl;\nexport const CacheControl = {\n parse: CacheLib.parse,\n stringify: CacheLib.stringify,\n};\n\n/**\n * - See https://github.com/prettymuchbryce/http-status-codes\n */\nexport { StatusCodes } from \"http-status-codes\";\n\n/**\n * Standard HTTP header names and common values.\n */\nexport namespace HttpHeader {\n export const VARY = \"Vary\";\n export const CONTENT_TYPE = \"Content-Type\";\n export const CACHE_CONTROL = \"Cache-Control\";\n\n // Security Headers\n export const X_CONTENT_TYPE_OPTIONS = \"X-Content-Type-Options\"; // usually \"nosniff\"\n export const X_FRAME_OPTIONS = \"X-Frame-Options\"; // e.g. \"DENY\" or \"SAMEORIGIN\"\n export const STRICT_TRANSPORT_SECURITY = \"Strict-Transport-Security\"; // e.g. \"max-age=63072000; includeSubDomains; preload\"\n export const CONTENT_SECURITY_POLICY = \"Content-Security-Policy\"; // fine-grained script/style/image restrictions\n export const REFERRER_POLICY = \"Referrer-Policy\"; // e.g. \"no-referrer\", \"strict-origin-when-cross-origin\"\n export const PERMISSIONS_POLICY = \"Permissions-Policy\"; // formerly Feature-Policy, controls APIs like geolocation/camera\n\n // Values\n export const NOSNIFF = \"nosniff\";\n export const ORIGIN = \"Origin\";\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/**\n * Standard HTTP request methods.\n */\nexport enum Method {\n GET = \"GET\",\n PUT = \"PUT\",\n POST = \"POST\",\n PATCH = \"PATCH\",\n DELETE = \"DELETE\",\n HEAD = \"HEAD\",\n OPTIONS = \"OPTIONS\",\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: string): value is Method {\n return METHOD_SET.has(value);\n}\n\n/**\n * Returns the proper Content-Type string for a given media type.\n * Appends `charset=utf-8` for text-based types that require it.\n *\n * @param type - The media type.\n * @returns A string suitable for the `Content-Type` header.\n */\nexport function getContentType(type: MediaType): string {\n if (ADD_CHARSET.has(type)) {\n return `${type}; charset=utf-8`;\n }\n return type;\n}\n\n/**\n * Common media types types used for HTTP headers.\n */\nexport enum MediaType {\n PLAIN_TEXT = \"text/plain\",\n HTML = \"text/html\",\n CSS = \"text/css\",\n CSV = \"text/csv\",\n XML = \"text/xml\",\n MARKDOWN = \"text/markdown\",\n RICH_TEXT = \"text/richtext\",\n JSON = \"application/json\",\n XML_APP = \"application/xml\",\n YAML = \"application/x-yaml\",\n FORM_URLENCODED = \"application/x-www-form-urlencoded\",\n NDJSON = \"application/x-ndjson\",\n MSGPACK = \"application/x-msgpack\",\n PROTOBUF = \"application/x-protobuf\",\n MULTIPART_FORM_DATA = \"multipart/form-data\",\n MULTIPART_MIXED = \"multipart/mixed\",\n MULTIPART_ALTERNATIVE = \"multipart/alternative\",\n MULTIPART_DIGEST = \"multipart/digest\",\n MULTIPART_RELATED = \"multipart/related\",\n MULTIPART_SIGNED = \"multipart/signed\",\n MULTIPART_ENCRYPTED = \"multipart/encrypted\",\n OCTET_STREAM = \"application/octet-stream\",\n PDF = \"application/pdf\",\n ZIP = \"application/zip\",\n GZIP = \"application/gzip\",\n MSWORD = \"application/msword\",\n DOCX = \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n EXCEL = \"application/vnd.ms-excel\",\n XLSX = \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n POWERPOINT = \"application/vnd.ms-powerpoint\",\n PPTX = \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n ICO = \"image/x-icon\",\n ICO_MS = \"image/vnd.microsoft.icon\",\n GIF = \"image/gif\",\n PNG = \"image/png\",\n JPEG = \"image/jpeg\",\n WEBP = \"image/webp\",\n SVG = \"image/svg+xml\",\n HEIF = \"image/heif\",\n AVIF = \"image/avif\",\n EVENT_STREAM = \"text/event-stream\",\n TAR = \"application/x-tar\",\n BZIP2 = \"application/x-bzip2\",\n}\n\n/**\n * A set of media types that require a `charset` parameter when setting\n * the `Content-Type` header.\n *\n * This includes common text-based media types such as HTML, CSS, JSON,\n * XML, CSV, Markdown, and others.\n */\nconst ADD_CHARSET: Set<MediaType> = new Set([\n MediaType.PLAIN_TEXT,\n MediaType.HTML,\n MediaType.CSS,\n MediaType.CSV,\n MediaType.MARKDOWN,\n MediaType.XML,\n MediaType.JSON,\n MediaType.XML_APP,\n MediaType.FORM_URLENCODED,\n MediaType.NDJSON,\n MediaType.RICH_TEXT,\n MediaType.SVG,\n]);\n\n/**\n * Sets a header on the given Headers object.\n *\n * - If `value` is an array, duplicates and empty strings are removed.\n * - If the resulting value array 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()))).filter((v) => v.length);\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 *\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) return;\n\n const existing = headers.get(key);\n if (existing) {\n const merged = existing.split(\",\").map((v) => v.trim());\n values.forEach((v) => merged.push(v.trim()));\n setHeader(headers, key, merged);\n } else {\n setHeader(headers, key, values);\n }\n}\n\n/**\n * Normalizes a URL string for use as a consistent cache key.\n *\n * - Sorts query parameters alphabetically so `?b=2&a=1` and `?a=1&b=2` are treated the same.\n * - Strips fragment identifiers (`#...`) since they are not sent in HTTP requests.\n * - Leaves protocol, host, path, and query values intact.\n *\n * @param url The original URL string to normalize.\n * @returns A normalized URL string suitable for hashing or direct cache key use.\n */\nexport function normalizeUrl(url: string): URL {\n const u = new URL(url);\n\n const params = [...u.searchParams.entries()];\n params.sort(([a], [b]) => a.localeCompare(b));\n\n u.search = params\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)\n .join(\"&\");\n u.hash = \"\";\n\n return u;\n}\n\n/**\n * Encodes a given string into Base64 using UTF-8 encoding.\n *\n * This function correctly handles any Unicode characters.\n *\n * @param raw - The input string to encode.\n * @returns The Base64-encoded string.\n */\nexport function toBase64(raw: string): string {\n const bytes = new TextEncoder().encode(raw);\n let binary = \"\";\n for (const b of bytes) {\n binary += String.fromCharCode(b);\n }\n return btoa(binary);\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, mergeHeader, Method, setHeader } from \"./common\";\n\n/**\n * Provides information about the CORS policy for the current request.\n */\nexport interface CorsProvider {\n /** Returns the origin of the request, or null if none is provided. */\n getOrigin(): string | null;\n\n /** Returns a list of allowed origins. */\n getAllowOrigins(): string[];\n\n /** Returns true if any origin is allowed (`*`). */\n allowAnyOrigin(): boolean;\n\n /** Returns the HTTP methods allowed by CORS. */\n getAllowMethods(): Method[];\n\n /** Returns the HTTP headers allowed by CORS. */\n getAllowHeaders(): string[];\n\n /** Returns the HTTP headers that should be exposed to the browser. */\n getExposeHeaders(): string[];\n\n /** Returns the max age (in seconds) for CORS preflight caching. */\n getMaxAge(): number;\n}\n\n/**\n * Constants for common CORS headers.\n */\nexport namespace Cors {\n export const ALLOW_ORIGIN = \"Access-Control-Allow-Origin\";\n export const ALLOW_CREDENTIALS = \"Access-Control-Allow-Credentials\";\n export const EXPOSE_HEADERS = \"Access-Control-Expose-Headers\";\n export const ALLOW_HEADERS = \"Access-Control-Allow-Headers\";\n export const ALLOW_METHODS = \"Access-Control-Allow-Methods\";\n export const MAX_AGE = \"Access-Control-Max-Age\";\n export const ALLOW_ALL_ORIGINS = \"*\";\n}\n\n/**\n * Adds or updates CORS headers on a Headers object according to the provided policy.\n *\n * Behavior:\n * - Removes any existing CORS headers to avoid stale values.\n * - If the request has no origin, the function exits early.\n * - If wildcard `*` is allowed, sets Access-Control-Allow-Origin to `*`.\n * - If the origin is explicitly allowed, sets the correct headers including credentials and Vary: Origin.\n * - Optional headers (Expose-Headers, Allow-Headers, Allow-Methods, Max-Age) are always applied.\n *\n * @param cors The CorsProvider instance that determines allowed origins and headers\n * @param headers The Headers object to update\n */\nexport function addCorsHeaders(cors: CorsProvider, headers: Headers): void {\n // Remove stale headers if from cache\n deleteCorsHeaders(headers);\n\n const origin = cors.getOrigin();\n if (!origin) return;\n\n if (cors.allowAnyOrigin()) {\n setHeader(headers, Cors.ALLOW_ORIGIN, Cors.ALLOW_ALL_ORIGINS);\n } else if (cors.getAllowOrigins().includes(origin)) {\n setHeader(headers, Cors.ALLOW_ORIGIN, origin);\n setHeader(headers, Cors.ALLOW_CREDENTIALS, \"true\");\n mergeHeader(headers, HttpHeader.VARY, HttpHeader.ORIGIN);\n }\n\n // Optional headers always applied\n mergeHeader(headers, Cors.EXPOSE_HEADERS, cors.getExposeHeaders());\n setHeader(headers, Cors.ALLOW_HEADERS, cors.getAllowHeaders());\n setHeader(headers, Cors.ALLOW_METHODS, cors.getAllowMethods());\n setHeader(headers, Cors.MAX_AGE, String(cors.getMaxAge()));\n}\n\n/**\n * Deletes all standard CORS headers from the given Headers object.\n * Useful for cleaning cached responses or resetting headers before reapplying CORS.\n *\n * @param headers The Headers object to clean\n */\nexport function deleteCorsHeaders(headers: Headers) {\n headers.delete(Cors.ALLOW_ORIGIN);\n headers.delete(Cors.ALLOW_CREDENTIALS);\n headers.delete(Cors.EXPOSE_HEADERS);\n headers.delete(Cors.ALLOW_METHODS);\n headers.delete(Cors.MAX_AGE);\n}\n\n/**\n * Returns a new Response with CORS headers applied according to the policy.\n * Original response is not mutated.\n *\n * @param cors The CorsProvider instance for policy\n * @param res The original Response object\n * @returns A new Response with updated CORS headers\n */\nexport function withCorsHeaders(cors: CorsProvider, res: Response): Response {\n const headers = new Headers(res.headers);\n addCorsHeaders(cors, headers);\n return new Response(res.body, {\n status: res.status,\n statusText: res.statusText,\n headers,\n });\n}\n\n/**\n * Returns a new Response with all standard CORS headers removed.\n * Useful for storing responses in a cache without per-request CORS headers.\n *\n * @param res The original Response object\n * @returns A new Response without CORS headers\n */\nexport function withoutCorsHeaders(res: Response): Response {\n const headers = new Headers(res.headers);\n deleteCorsHeaders(headers);\n return new Response(res.body, {\n status: res.status,\n statusText: res.statusText,\n headers,\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 \"./worker\";\n\n/**\n * A type-safe Cloudflare Worker handler where `fetch` is required.\n *\n * Extends `ExportedHandler` but guarantees that the `fetch` method exists\n * and has the correct signature for Cloudflare Worker invocation.\n *\n * @template E - The type of environment bindings passed to the worker. Defaults to `Env`.\n */\ninterface FetchHandler<E = Env> extends ExportedHandler<E> {\n /**\n * Handles an incoming request and produces a response.\n *\n * @param request - The incoming `Request` object.\n * @param env - Environment bindings (e.g., KV namespaces, secrets, Durable Objects).\n * @param ctx - Optional execution context for background tasks (`waitUntil`).\n * @returns A `Promise` that resolves to the response.\n */\n fetch: (request: Request, env: E, ctx: ExecutionContext) => Promise<Response>;\n}\n\n/**\n * Provides the foundational structure for handling requests, environment bindings,\n * and the worker execution context. Subclasses are expected to implement the\n * `fetch` method to handle the request and return a Response.\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), if available.\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 protected get request(): Request {\n return this._request;\n }\n\n /** Environment bindings (e.g., KV, secrets, or other globals) */\n protected get env(): Env {\n return this._env;\n }\n\n /** Optional execution context for background tasks or `waitUntil` */\n protected get ctx(): ExecutionContext | undefined {\n return this._ctx;\n }\n\n /**\n * Process the request and produce a Response.\n * Subclasses must implement this method.\n *\n * @returns A Promise resolving to the Response for the request\n */\n public abstract fetch(): Promise<Response>;\n\n /**\n * Ignite your `Worker` implementation into a Cloudflare-compatible handler.\n *\n * @returns A `FetchHandler` that launches a new worker instance for each request.\n *\n * @example\n * ```ts\n * export default MyWorker.ignite();\n * ```\n */\n public static ignite<T extends new (...args: any) => BaseWorker>(this: T): FetchHandler {\n return {\n fetch: (req: Request, env: Env, ctx: ExecutionContext) =>\n new this(req, 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 { BaseWorker } from \"./base-worker\";\nimport { Method, Time } from \"./common\";\nimport { CorsProvider } from \"./cors\";\n\n/**\n * Abstract base class for Workers to provide a default CORS policy.\n *\n * Implements the `CorsProvider` interface and provides a standard policy:\n * - Allows all origins (`*`) by default.\n * - Allows GET, OPTIONS, and HEAD methods.\n * - Allows the `Content-Type` header.\n * - Exposes no additional headers.\n * - Sets CORS preflight max-age to one week.\n *\n * Subclasses can override any of the methods to customize the CORS behavior.\n */\nexport abstract class CorsWorker extends BaseWorker implements CorsProvider {\n public getAllowOrigins(): string[] {\n return [\"*\"];\n }\n\n public allowAnyOrigin(): boolean {\n return this.getAllowOrigins().includes(\"*\");\n }\n\n public getAllowMethods(): Method[] {\n return [Method.GET, Method.OPTIONS, Method.HEAD];\n }\n\n public getAllowHeaders(): string[] {\n return [\"Content-Type\"];\n }\n\n public getExposeHeaders(): string[] {\n return [];\n }\n\n public getMaxAge(): number {\n return Time.Week;\n }\n\n public getOrigin(): string | null {\n return this.request.headers.get(\"Origin\");\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, normalizeUrl } from \"./common\";\nimport { withCorsHeaders, withoutCorsHeaders } from \"./cors\";\nimport { CorsWorker } from \"./cors-worker\";\n\n/**\n * Abstract base class for Workers that support caching of GET responses.\n *\n * Features:\n * - URL-based caching using `Request.url`.\n * - Removes per-request CORS headers before storing in cache.\n * - Dynamically applies correct CORS headers when retrieving cached responses.\n * - Only caches successful GET requests.\n */\nexport abstract class CacheWorker extends CorsWorker {\n /**\n * Returns the cache key for the current request.\n * By default, this is the normalized request URL.\n *\n * @returns A URL or RequestInfo to use as the cache key\n */\n protected getCacheKey(): URL | RequestInfo {\n return normalizeUrl(this.request.url);\n }\n\n /**\n * Retrieves a cached Response for the current request, if one exists.\n *\n * Behavior:\n * - Only GET requests are cached.\n * - If a cached response is found, CORS headers are applied dynamically\n * using `withCorsHeaders` before returning.\n * - Returns undefined if no cached response is found or if request is not GET.\n *\n * @param cacheName Optional name of the cache to use; defaults to `caches.default`.\n * @returns A Promise resolving to a Response with correct CORS headers, or undefined.\n */\n protected async getCachedResponse(cacheName?: string): Promise<Response | undefined> {\n if (this.request.method !== Method.GET) return;\n\n const cache = cacheName ? await caches.open(cacheName) : caches.default;\n\n const response = await cache.match(this.getCacheKey());\n return response ? withCorsHeaders(this, response) : undefined;\n }\n\n /**\n * Stores a Response in the cache for the current request.\n *\n * Behavior:\n * - Only caches successful GET responses (`response.ok === true`).\n * - Removes all CORS headers before storing using `withoutCorsHeaders`.\n * - Uses `ctx.waitUntil` to store asynchronously without blocking the worker.\n *\n * @param response The Response to cache\n * @param cacheName Optional name of the cache to use; defaults to `caches.default`.\n */\n protected async setCachedResponse(response: Response, cacheName?: string): Promise<void> {\n if (!response.ok) return;\n if (this.request.method !== Method.GET) return;\n\n const cache = cacheName ? await caches.open(cacheName) : caches.default;\n this.ctx?.waitUntil(cache.put(this.getCacheKey(), withoutCorsHeaders(response.clone())));\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 {\n CacheControl,\n getContentType,\n HttpHeader,\n mergeHeader,\n Method,\n MediaType,\n setHeader,\n} from \"./common\";\nimport { addCorsHeaders, CorsProvider } from \"./cors\";\n\nexport interface ErrorJson {\n status: number;\n error: string;\n details: string;\n}\n\nabstract class BaseResponse {\n public headers: Headers = new Headers();\n public body: BodyInit | null;\n public status: StatusCodes = StatusCodes.OK;\n public statusText?: string;\n public mediaType?: MediaType;\n\n constructor(content: BodyInit | null = null) {\n this.body = this.status === StatusCodes.NO_CONTENT ? null : content;\n }\n\n protected get responseInit(): ResponseInit {\n return {\n headers: this.headers,\n status: this.status,\n statusText: this.statusText ?? getReasonPhrase(this.status),\n };\n }\n\n public setHeader(key: string, value: string | string[]): void {\n setHeader(this.headers, key, value);\n }\n\n public mergeHeader(key: string, value: string | string[]): void {\n mergeHeader(this.headers, key, value);\n }\n\n public addContentType() {\n if (this.mediaType) {\n this.headers.set(HttpHeader.CONTENT_TYPE, getContentType(this.mediaType));\n }\n }\n}\n\nabstract class CorsResponse extends BaseResponse {\n constructor(public readonly cors: CorsProvider, content: BodyInit | null = null) {\n super(content);\n }\n\n protected addCorsHeaders(): void {\n addCorsHeaders(this.cors, this.headers);\n }\n}\n\nabstract class CacheResponse extends CorsResponse {\n constructor(cors: CorsProvider, body: BodyInit | null = null, public cache?: CacheControl) {\n super(cors, body);\n }\n\n protected addCacheHeader(): void {\n if (this.cache) {\n this.headers.set(HttpHeader.CACHE_CONTROL, CacheControl.stringify(this.cache));\n }\n }\n}\n\nexport abstract class WorkerResponse extends CacheResponse {\n public createResponse(): Response {\n this.addCorsHeaders();\n this.addCacheHeader();\n this.addSecurityHeaders();\n\n const body = this.status === StatusCodes.NO_CONTENT ? null : this.body;\n if (body) this.addContentType();\n return new Response(body, this.responseInit);\n }\n\n protected addSecurityHeaders(): void {\n this.setHeader(HttpHeader.X_CONTENT_TYPE_OPTIONS, HttpHeader.NOSNIFF);\n }\n}\n\nexport class ClonedResponse extends WorkerResponse {\n constructor(cors: CorsProvider, response: Response, cache?: CacheControl) {\n const clone = response.clone();\n super(cors, clone.body, cache);\n this.headers = new Headers(clone.headers);\n this.status = clone.status;\n this.statusText = clone.statusText;\n }\n}\n\nexport class SuccessResponse extends WorkerResponse {\n constructor(\n cors: CorsProvider,\n body: BodyInit | null = null,\n cache?: CacheControl,\n status: StatusCodes = StatusCodes.OK\n ) {\n super(cors, body, cache);\n this.status = status;\n }\n}\n\nexport class JsonResponse extends SuccessResponse {\n constructor(\n cors: CorsProvider,\n json: unknown = {},\n cache?: CacheControl,\n status: StatusCodes = StatusCodes.OK\n ) {\n super(cors, JSON.stringify(json), cache, status);\n this.mediaType = MediaType.JSON;\n }\n}\n\nexport class HtmlResponse extends SuccessResponse {\n constructor(\n cors: CorsProvider,\n body: string,\n cache?: CacheControl,\n status: StatusCodes = StatusCodes.OK\n ) {\n super(cors, body, cache, status);\n this.mediaType = MediaType.HTML;\n }\n}\n\nexport class TextResponse extends SuccessResponse {\n constructor(\n cors: CorsProvider,\n content: string,\n cache?: CacheControl,\n status: StatusCodes = StatusCodes.OK\n ) {\n super(cors, content, cache, status);\n this.mediaType = MediaType.PLAIN_TEXT;\n }\n}\n\n/**\n * Removes the body from a GET response.\n */\nexport class Head extends WorkerResponse {\n constructor(cors: CorsProvider, get: Response) {\n super(cors);\n this.headers = new Headers(get.headers);\n }\n}\n\nexport class Options extends SuccessResponse {\n constructor(cors: CorsProvider) {\n super(cors, null, undefined, StatusCodes.NO_CONTENT);\n this.setHeader(\"Allow\", this.cors.getAllowMethods());\n }\n}\n\nexport class HttpError extends JsonResponse {\n constructor(cors: CorsProvider, status: StatusCodes, protected readonly details?: string) {\n const cache: CacheControl = {\n \"no-cache\": true,\n \"no-store\": true,\n \"must-revalidate\": true,\n \"max-age\": 0,\n };\n super(cors, undefined, cache, status);\n }\n\n public get json(): ErrorJson {\n return {\n status: this.status,\n error: getReasonPhrase(this.status),\n details: this.details ?? getReasonPhrase(this.status),\n };\n }\n\n public override createResponse(): Response {\n this.body = JSON.stringify(this.json);\n return super.createResponse();\n }\n}\n\nexport class BadRequest extends HttpError {\n constructor(cors: CorsProvider, details?: string) {\n super(cors, StatusCodes.BAD_REQUEST, details);\n }\n}\n\nexport class Unauthorized extends HttpError {\n constructor(cors: CorsProvider, details?: string) {\n super(cors, StatusCodes.UNAUTHORIZED, details);\n }\n}\n\nexport class Forbidden extends HttpError {\n constructor(cors: CorsProvider, details?: string) {\n super(cors, StatusCodes.FORBIDDEN, details);\n }\n}\n\nexport class NotFound extends HttpError {\n constructor(cors: CorsProvider, details?: string) {\n super(cors, StatusCodes.NOT_FOUND, details);\n }\n}\n\nexport class MethodNotAllowed extends HttpError {\n constructor(cors: CorsProvider, method: string) {\n super(cors, StatusCodes.METHOD_NOT_ALLOWED, `${method} method not allowed.`);\n this.setHeader(\"Allow\", this.cors.getAllowMethods());\n }\n\n public override get json(): ErrorJson & { allowed: Method[] } {\n return {\n ...super.json,\n allowed: this.cors.getAllowMethods(),\n };\n }\n}\n\nexport class InternalServerError extends HttpError {\n constructor(cors: CorsProvider, details?: string) {\n super(cors, StatusCodes.INTERNAL_SERVER_ERROR, details);\n }\n}\n\nexport class NotImplemented extends HttpError {\n constructor(cors: CorsProvider) {\n super(cors, StatusCodes.NOT_IMPLEMENTED);\n }\n}\n\nexport class ServiceUnavailable extends HttpError {\n constructor(cors: CorsProvider, details?: string) {\n super(cors, 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 { CacheWorker } from \"./cache-worker\";\nimport { isMethod, Method } from \"./common\";\nimport { CorsProvider } from \"./cors\";\nimport {\n Head,\n InternalServerError,\n MethodNotAllowed,\n NotImplemented,\n Options,\n WorkerResponse,\n} from \"./response\";\n\nexport abstract class BasicWorker extends CacheWorker {\n public async fetch(): Promise<Response> {\n if (!this.isAllowed(this.request.method)) {\n return this.getResponse(MethodNotAllowed, this.request.method);\n }\n\n try {\n return await this.dispatch();\n } catch (error) {\n return this.getResponse(InternalServerError, String(error));\n }\n }\n\n protected async dispatch(request: Request = this.request): Promise<Response> {\n const method = request.method as Method;\n const handler: Record<Method, () => Promise<Response>> = {\n GET: () => this.get(),\n PUT: () => this.put(),\n POST: () => this.post(),\n PATCH: () => this.patch(),\n DELETE: () => this.delete(),\n HEAD: () => this.head(),\n OPTIONS: () => this.options(),\n };\n return (handler[method] ?? (() => this.getResponse(MethodNotAllowed, method)))();\n }\n\n protected async get(): Promise<Response> {\n return this.getResponse(NotImplemented);\n }\n\n protected async put(): Promise<Response> {\n return this.getResponse(NotImplemented);\n }\n\n protected async post(): Promise<Response> {\n return this.getResponse(NotImplemented);\n }\n\n protected async patch(): Promise<Response> {\n return this.getResponse(NotImplemented);\n }\n\n protected async delete(): Promise<Response> {\n return this.getResponse(NotImplemented);\n }\n\n protected async options(): Promise<Response> {\n return this.getResponse(Options);\n }\n\n protected async head(): Promise<Response> {\n // Dispatch a new GET request created from the HEAD request\n // and return the GET response with the body removed.\n return this.getResponse(\n Head,\n await this.dispatch(new Request(this.request, { method: Method.GET }))\n );\n }\n\n protected async getResponse<\n T extends WorkerResponse,\n Ctor extends new (cors: CorsProvider, ...args: any[]) => T\n >(\n ResponseClass: Ctor,\n ...args: ConstructorParameters<Ctor> extends [CorsProvider, ...infer R] ? R : never\n ): Promise<Response> {\n const response = new ResponseClass(this, ...args).createResponse();\n this.setCachedResponse(response);\n return response;\n }\n\n public isAllowed(method: string): boolean {\n return isMethod(method) && this.getAllowMethods().includes(method);\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 \"./common\";\n\n/**\n * A callback function for a route.\n *\n * @param matches - Captured groups from the route RegExp, or the full match at index 0\n * @returns A Response object or a Promise that resolves to a Response\n */\nexport type RouteCallback = (...matches: string[]) => Response | Promise<Response>;\n\n/**\n * Tuple used to initialize a route.\n *\n * [HTTP method, path pattern (string or RegExp), callback function]\n */\nexport type RouteInit = [Method, RegExp | string, RouteCallback];\n\n/**\n * Represents a single route with a pattern and a callback.\n */\nexport class Route {\n public readonly pattern: RegExp;\n\n /**\n * @param pattern - A RegExp or string used to match the request path\n * @param callback - Function to handle requests matching the pattern\n */\n constructor(pattern: RegExp | string, public readonly callback: RouteCallback) {\n this.pattern = new RegExp(pattern);\n }\n}\n\n/**\n * A collection of routes grouped by HTTP method.\n *\n * Supports adding routes and retrieving the first matching route for a given method and URL.\n */\nexport class Routes {\n private readonly map = new Map<Method, Route[]>();\n\n /**\n * Adds a route to the collection under the given HTTP method.\n *\n * @param method - HTTP method (GET, POST, etc.)\n * @param route - Route instance to add\n * @returns The Routes instance (for chaining)\n */\n public add(method: Method, route: Route): this {\n const existing = this.map.get(method);\n if (existing) {\n existing.push(route);\n } else {\n this.map.set(method, [route]);\n }\n return this;\n }\n\n /**\n * Finds the first route that matches the given method and URL.\n *\n * @param method - HTTP method of the request\n * @param url - Full URL string of the request\n * @returns The first matching Route, or undefined if none match\n */\n public get(method: Method, url: string): Route | undefined {\n const routes = this.map.get(method);\n if (!routes) return undefined;\n\n const pathname = new URL(url).pathname;\n return routes.find(({ pattern }) => pattern.test(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 { BasicWorker } from \"./basic-worker\";\nimport { Method } from \"./common\";\nimport { NotFound } from \"./response\";\nimport { Route, Routes, RouteInit, RouteCallback } from \"./routes\";\n\nexport abstract class RoutedWorker extends BasicWorker {\n private readonly routes: Routes = new Routes();\n\n protected initialize(routes: RouteInit[]) {\n routes.forEach(([method, pattern, callback]) => {\n this.add(method, pattern, callback);\n });\n }\n\n protected add(method: Method, pattern: RegExp | string, callback: RouteCallback) {\n this.routes.add(method, new Route(pattern, callback));\n return this;\n }\n\n protected async dispatch(request: Request = this.request): Promise<Response> {\n const route = this.routes.get(request.method as Method, request.url);\n if (!route) return super.dispatch(request);\n\n const match = new URL(request.url).pathname.match(route.pattern) ?? [];\n return route.callback.call(this, ...match);\n }\n\n protected override async get(): Promise<Response> {\n return this.getResponse(NotFound);\n }\n\n protected override async put(): Promise<Response> {\n return this.getResponse(NotFound);\n }\n\n protected override async post(): Promise<Response> {\n return this.getResponse(NotFound);\n }\n\n protected override async patch(): Promise<Response> {\n return this.getResponse(NotFound);\n }\n\n protected override async delete(): Promise<Response> {\n return this.getResponse(NotFound);\n }\n}\n"],"mappings":"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","names":["HttpHeader","Method","MediaType","Cors","StatusCodes","StatusCodes"]}
1
+ {"version":3,"sources":["../src/common.ts","../src/cors.ts","../src/base-worker.ts","../src/cors-worker.ts","../src/cache-worker.ts","../src/response.ts","../src/basic-worker.ts","../src/routes.ts","../src/routed-worker.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\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\n/**\n * https://github.com/prettymuchbryce/http-status-codes\n */\nexport { StatusCodes } from \"http-status-codes\";\n\n/**\n * Standard HTTP header names and common values.\n */\nexport namespace HttpHeader {\n export const VARY = \"Vary\";\n export const CONTENT_TYPE = \"Content-Type\";\n export const CACHE_CONTROL = \"Cache-Control\";\n\n // Security Headers\n export const X_CONTENT_TYPE_OPTIONS = \"X-Content-Type-Options\"; // usually \"nosniff\"\n export const X_FRAME_OPTIONS = \"X-Frame-Options\"; // e.g. \"DENY\" or \"SAMEORIGIN\"\n export const STRICT_TRANSPORT_SECURITY = \"Strict-Transport-Security\"; // e.g. \"max-age=63072000; includeSubDomains; preload\"\n export const CONTENT_SECURITY_POLICY = \"Content-Security-Policy\"; // fine-grained script/style/image restrictions\n export const REFERRER_POLICY = \"Referrer-Policy\"; // e.g. \"no-referrer\", \"strict-origin-when-cross-origin\"\n export const PERMISSIONS_POLICY = \"Permissions-Policy\"; // formerly Feature-Policy, controls APIs like geolocation/camera\n\n // Values\n export const NOSNIFF = \"nosniff\";\n export const ORIGIN = \"Origin\";\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/**\n * Standard HTTP request methods.\n */\nexport enum Method {\n GET = \"GET\",\n PUT = \"PUT\",\n POST = \"POST\",\n PATCH = \"PATCH\",\n DELETE = \"DELETE\",\n HEAD = \"HEAD\",\n OPTIONS = \"OPTIONS\",\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: string): value is Method {\n return METHOD_SET.has(value);\n}\n\n/**\n * Returns the proper Content-Type string for a given media type.\n * Appends `charset=utf-8` for text-based types that require it.\n *\n * @param type - The media type.\n * @returns A string suitable for the `Content-Type` header.\n */\nexport function getContentType(type: MediaType): string {\n if (ADD_CHARSET.has(type)) {\n return `${type}; charset=utf-8`;\n }\n return type;\n}\n\n/**\n * Common media types types used for HTTP headers.\n */\nexport enum MediaType {\n PLAIN_TEXT = \"text/plain\",\n HTML = \"text/html\",\n CSS = \"text/css\",\n CSV = \"text/csv\",\n XML = \"text/xml\",\n MARKDOWN = \"text/markdown\",\n RICH_TEXT = \"text/richtext\",\n JSON = \"application/json\",\n XML_APP = \"application/xml\",\n YAML = \"application/x-yaml\",\n FORM_URLENCODED = \"application/x-www-form-urlencoded\",\n NDJSON = \"application/x-ndjson\",\n MSGPACK = \"application/x-msgpack\",\n PROTOBUF = \"application/x-protobuf\",\n MULTIPART_FORM_DATA = \"multipart/form-data\",\n MULTIPART_MIXED = \"multipart/mixed\",\n MULTIPART_ALTERNATIVE = \"multipart/alternative\",\n MULTIPART_DIGEST = \"multipart/digest\",\n MULTIPART_RELATED = \"multipart/related\",\n MULTIPART_SIGNED = \"multipart/signed\",\n MULTIPART_ENCRYPTED = \"multipart/encrypted\",\n OCTET_STREAM = \"application/octet-stream\",\n PDF = \"application/pdf\",\n ZIP = \"application/zip\",\n GZIP = \"application/gzip\",\n MSWORD = \"application/msword\",\n DOCX = \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n EXCEL = \"application/vnd.ms-excel\",\n XLSX = \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n POWERPOINT = \"application/vnd.ms-powerpoint\",\n PPTX = \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n ICO = \"image/x-icon\",\n ICO_MS = \"image/vnd.microsoft.icon\",\n GIF = \"image/gif\",\n PNG = \"image/png\",\n JPEG = \"image/jpeg\",\n WEBP = \"image/webp\",\n SVG = \"image/svg+xml\",\n HEIF = \"image/heif\",\n AVIF = \"image/avif\",\n EVENT_STREAM = \"text/event-stream\",\n TAR = \"application/x-tar\",\n BZIP2 = \"application/x-bzip2\",\n}\n\n/**\n * A set of media types that require a `charset` parameter when setting\n * the `Content-Type` header.\n *\n * This includes common text-based media types such as HTML, CSS, JSON,\n * XML, CSV, Markdown, and others.\n */\nconst ADD_CHARSET: Set<MediaType> = new Set([\n MediaType.PLAIN_TEXT,\n MediaType.HTML,\n MediaType.CSS,\n MediaType.CSV,\n MediaType.MARKDOWN,\n MediaType.XML,\n MediaType.JSON,\n MediaType.XML_APP,\n MediaType.FORM_URLENCODED,\n MediaType.NDJSON,\n MediaType.RICH_TEXT,\n MediaType.SVG,\n]);\n\n/**\n * Sets a header on the given Headers object.\n *\n * - If `value` is an array, duplicates and empty strings are removed.\n * - If the resulting value array 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()))).filter((v) => v.length);\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 *\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) return;\n\n const existing = headers.get(key);\n if (existing) {\n const merged = existing.split(\",\").map((v) => v.trim());\n values.forEach((v) => merged.push(v.trim()));\n setHeader(headers, key, merged);\n } else {\n setHeader(headers, key, values);\n }\n}\n\n/**\n * Normalizes a URL string for use as a consistent cache key.\n *\n * - Sorts query parameters alphabetically so `?b=2&a=1` and `?a=1&b=2` are treated the same.\n * - Strips fragment identifiers (`#...`) since they are not sent in HTTP requests.\n * - Leaves protocol, host, path, and query values intact.\n *\n * @param url The original URL string to normalize.\n * @returns A normalized URL string suitable for hashing or direct cache key use.\n */\nexport function normalizeUrl(url: string): URL {\n const u = new URL(url);\n\n const params = [...u.searchParams.entries()];\n params.sort(([a], [b]) => a.localeCompare(b));\n\n u.search = params\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)\n .join(\"&\");\n u.hash = \"\";\n\n return u;\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, mergeHeader, Method, setHeader } from \"./common\";\n\n/**\n * Provides information about the CORS policy for the current request.\n */\nexport interface CorsProvider {\n /** Returns the origin of the request, or null if none is provided. */\n getOrigin(): string | null;\n\n /** Returns a list of allowed origins. */\n getAllowOrigins(): string[];\n\n /** Returns true if any origin is allowed (`*`). */\n allowAnyOrigin(): boolean;\n\n /** Returns the HTTP methods allowed by CORS. */\n getAllowMethods(): Method[];\n\n /** Returns the HTTP headers allowed by CORS. */\n getAllowHeaders(): string[];\n\n /** Returns the HTTP headers that should be exposed to the browser. */\n getExposeHeaders(): string[];\n\n /** Returns the max age (in seconds) for CORS preflight caching. */\n getMaxAge(): number;\n}\n\n/**\n * Constants for common CORS headers.\n */\nexport namespace Cors {\n export const ALLOW_ORIGIN = \"Access-Control-Allow-Origin\";\n export const ALLOW_CREDENTIALS = \"Access-Control-Allow-Credentials\";\n export const EXPOSE_HEADERS = \"Access-Control-Expose-Headers\";\n export const ALLOW_HEADERS = \"Access-Control-Allow-Headers\";\n export const ALLOW_METHODS = \"Access-Control-Allow-Methods\";\n export const MAX_AGE = \"Access-Control-Max-Age\";\n export const ALLOW_ALL_ORIGINS = \"*\";\n}\n\n/**\n * Adds or updates CORS headers on a Headers object according to the provided policy.\n *\n * Behavior:\n * - Removes any existing CORS headers to avoid stale values.\n * - If the request has no origin, the function exits early.\n * - If wildcard `*` is allowed, sets Access-Control-Allow-Origin to `*`.\n * - If the origin is explicitly allowed, sets the correct headers including credentials and Vary: Origin.\n * - Optional headers (Expose-Headers, Allow-Headers, Allow-Methods, Max-Age) are always applied.\n *\n * @param cors The CorsProvider instance that determines allowed origins and headers\n * @param headers The Headers object to update\n */\nexport function addCorsHeaders(cors: CorsProvider, headers: Headers): void {\n deleteCorsHeaders(headers);\n\n const origin = cors.getOrigin();\n if (!origin) return;\n\n if (cors.allowAnyOrigin()) {\n setHeader(headers, Cors.ALLOW_ORIGIN, Cors.ALLOW_ALL_ORIGINS);\n } else if (cors.getAllowOrigins().includes(origin)) {\n setHeader(headers, Cors.ALLOW_ORIGIN, origin);\n setHeader(headers, Cors.ALLOW_CREDENTIALS, \"true\");\n mergeHeader(headers, HttpHeader.VARY, HttpHeader.ORIGIN);\n }\n\n // Optional headers always applied\n mergeHeader(headers, Cors.EXPOSE_HEADERS, cors.getExposeHeaders());\n setHeader(headers, Cors.ALLOW_HEADERS, cors.getAllowHeaders());\n setHeader(headers, Cors.ALLOW_METHODS, cors.getAllowMethods());\n setHeader(headers, Cors.MAX_AGE, String(cors.getMaxAge()));\n}\n\n/**\n * Deletes all standard CORS headers from the given Headers object.\n * Useful for cleaning cached responses or resetting headers before reapplying CORS.\n *\n * @param headers The Headers object to clean\n */\nfunction deleteCorsHeaders(headers: Headers) {\n headers.delete(Cors.ALLOW_ORIGIN);\n headers.delete(Cors.ALLOW_CREDENTIALS);\n headers.delete(Cors.EXPOSE_HEADERS);\n headers.delete(Cors.ALLOW_METHODS);\n headers.delete(Cors.MAX_AGE);\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 \"./worker\";\n\n/**\n * A type-safe Cloudflare Worker handler where `fetch` is required.\n *\n * Extends `ExportedHandler` but guarantees that the `fetch` method exists\n * and has the correct signature for Cloudflare Worker invocation.\n *\n * @template E - The type of environment bindings passed to the worker. Defaults to `Env`.\n */\ninterface FetchHandler<E = Env> extends ExportedHandler<E> {\n /**\n * Handles an incoming request and produces a response.\n *\n * @param request - The incoming `Request` object.\n * @param env - Environment bindings (e.g., KV namespaces, secrets, Durable Objects).\n * @param ctx - Optional execution context for background tasks (`waitUntil`).\n * @returns A `Promise` that resolves to the response.\n */\n fetch: (request: Request, env: E, ctx: ExecutionContext) => Promise<Response>;\n}\n\n/**\n * Provides the foundational structure for handling requests, environment bindings,\n * and the worker execution context. Subclasses are expected to implement the\n * `fetch` method to handle the request and return a Response.\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), if available.\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 protected get request(): Request {\n return this._request;\n }\n\n /** Environment bindings (e.g., KV, secrets, or other globals) */\n protected get env(): Env {\n return this._env;\n }\n\n /** Optional execution context for background tasks or `waitUntil` */\n protected get ctx(): ExecutionContext | undefined {\n return this._ctx;\n }\n\n /**\n * Process the request and produce a Response.\n * Subclasses must implement this method.\n *\n * @returns A Promise resolving to the Response for the request\n */\n public abstract fetch(): Promise<Response>;\n\n /**\n * Ignite your `Worker` implementation into a Cloudflare-compatible handler.\n *\n * @returns A `FetchHandler` that launches a new worker instance for each request.\n *\n * @example\n * ```ts\n * export default MyWorker.ignite();\n * ```\n */\n public static ignite<T extends new (...args: any) => BaseWorker>(this: T): FetchHandler {\n return {\n fetch: (req: Request, env: Env, ctx: ExecutionContext) =>\n new this(req, 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 { BaseWorker } from \"./base-worker\";\nimport { Method, Time } from \"./common\";\nimport { CorsProvider } from \"./cors\";\n\n/**\n * Abstract base class for Workers to provide a default CORS policy.\n *\n * Implements the `CorsProvider` interface and provides a standard policy:\n * - Allows all origins (`*`) by default.\n * - Allows GET, OPTIONS, and HEAD methods.\n * - Allows the `Content-Type` header.\n * - Exposes no additional headers.\n * - Sets CORS preflight max-age to one week.\n *\n * Subclasses can override any of the methods to customize the CORS behavior.\n */\nexport abstract class CorsWorker extends BaseWorker implements CorsProvider {\n public getAllowOrigins(): string[] {\n return [\"*\"];\n }\n\n public allowAnyOrigin(): boolean {\n return this.getAllowOrigins().includes(\"*\");\n }\n\n public getAllowMethods(): Method[] {\n return [Method.GET, Method.OPTIONS, Method.HEAD];\n }\n\n public getAllowHeaders(): string[] {\n return [\"Content-Type\"];\n }\n\n public getExposeHeaders(): string[] {\n return [];\n }\n\n public getMaxAge(): number {\n return Time.Week;\n }\n\n public getOrigin(): string | null {\n return this.request.headers.get(\"Origin\");\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, normalizeUrl } from \"./common\";\nimport { addCorsHeaders, Cors } from \"./cors\";\nimport { CorsWorker } from \"./cors-worker\";\n\n/**\n * Abstract worker class that adds caching support for GET requests.\n *\n * Behavior:\n * - Caches successful GET responses (`response.ok === true`) in the selected cache.\n * - Strips CORS headers from cached responses; all other origin headers are preserved.\n * - Dynamically adds CORS headers to cached responses when returned to the client.\n *\n * Subclasses should override `getCacheKey()` to customize cache key generation if needed.\n */\nexport abstract class CacheWorker extends CorsWorker {\n /**\n * Returns the cache key for the current request.\n *\n * Behavior:\n * - By default, returns the normalized request URL.\n * - Query parameters are normalized so that the order does not affect the cache key.\n * For example, `?a=1&b=2` and `?b=2&a=1` produce the same cache key.\n *\n * Subclasses may override this method to implement custom cache key strategies.\n *\n * @returns {URL | RequestInfo} The URL or RequestInfo used as the cache key.\n */\n protected getCacheKey(): URL | RequestInfo {\n return normalizeUrl(this.request.url);\n }\n\n /**\n * Retrieves a cached Response for the current request, if one exists.\n *\n * Behavior:\n * - Only GET requests are considered.\n * - Returns a new Response with dynamic CORS headers applied via `addCacheHeaders`.\n * - Returns `undefined` if no cached response is found.\n * - Cloudflare dynamic headers (`CF-Cache-Status`, `Age`, `Connection`, etc.) will\n * always be present on the returned response, even though they are not stored in the cache.\n *\n * @param {string} [cacheName] Optional named cache; defaults to `caches.default`.\n * @returns {Promise<Response | undefined>} A Response with CORS headers, or undefined.\n * @see {@link setCachedResponse}\n * @see {@link getCacheKey}\n */\n protected async getCachedResponse(cacheName?: string): Promise<Response | undefined> {\n if (this.request.method !== Method.GET) return;\n\n const cache = cacheName ? await caches.open(cacheName) : caches.default;\n const response = await cache.match(this.getCacheKey());\n return response ? this.addCacheHeaders(response) : undefined;\n }\n\n /**\n * Stores a Response in the cache for the current request.\n *\n * Behavior:\n * - Only caches successful GET responses (`response.ok === true`).\n * - Strips headers via `removeCacheHeaders` before storing.\n * - Uses `ctx.waitUntil` to perform caching asynchronously without blocking the response.\n * - All other origin headers (e.g., Cache-Control, Expires) are preserved.\n *\n * @param {Response} response The Response to cache.\n * @param {string} [cacheName] Optional named cache; defaults to `caches.default`.\n * @see {@link getCachedResponse}\n * @see {@link getCacheKey}\n */\n protected async setCachedResponse(response: Response, cacheName?: string): Promise<void> {\n if (!response.ok) return;\n if (this.request.method !== Method.GET) return;\n\n const cache = cacheName ? await caches.open(cacheName) : caches.default;\n this.ctx?.waitUntil(\n cache.put(this.getCacheKey(), this.removeCacheHeaders(response.clone()))\n );\n }\n\n /**\n * Adds headers to a cached response.\n *\n * @param {Response} cached The cached Response.\n * @returns {Response} A new Response with dynamic CORS headers applied.\n * @see {@link removeCacheHeaders}\n */\n private addCacheHeaders(cached: Response): Response {\n const headers = new Headers(cached.headers);\n addCorsHeaders(this, headers);\n\n return new Response(cached.body, {\n status: cached.status,\n statusText: cached.statusText,\n headers,\n });\n }\n\n /**\n * Removes headers that should not be stored in the cache (currently only CORS headers).\n *\n * @param {Response} response The Response to clean before caching.\n * @returns {Response} A new Response with excluded headers removed.\n * @see {@link addCacheHeaders}\n */\n private removeCacheHeaders(response: Response): Response {\n const excludeSet = new Set(this.excludeCacheHeaders().map((h) => h.toLowerCase()));\n const headers = new Headers();\n\n for (const [key, value] of response.headers) {\n if (!excludeSet.has(key)) {\n headers.set(key, value);\n }\n }\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n /**\n * Returns the list of headers to exclude from the cached response.\n * By default, excludes only dynamic CORS headers.\n *\n * @returns {string[]} Array of header names to exclude.\n * @see {@link removeCacheHeaders}\n */\n protected excludeCacheHeaders(): string[] {\n return [\n Cors.ALLOW_ORIGIN,\n Cors.ALLOW_CREDENTIALS,\n Cors.EXPOSE_HEADERS,\n Cors.ALLOW_METHODS,\n Cors.MAX_AGE,\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 { getReasonPhrase, StatusCodes } from \"http-status-codes\";\nimport {\n CacheControl,\n getContentType,\n HttpHeader,\n mergeHeader,\n Method,\n MediaType,\n setHeader,\n} from \"./common\";\nimport { addCorsHeaders, CorsProvider } from \"./cors\";\n\nexport interface ErrorJson {\n status: number;\n error: string;\n details: string;\n}\n\nabstract class BaseResponse {\n public headers: Headers = new Headers();\n public body: BodyInit | null;\n public status: StatusCodes = StatusCodes.OK;\n public statusText?: string;\n public mediaType?: MediaType;\n\n constructor(content: BodyInit | null = null) {\n this.body = this.status === StatusCodes.NO_CONTENT ? null : content;\n }\n\n protected get responseInit(): ResponseInit {\n return {\n headers: this.headers,\n status: this.status,\n statusText: this.statusText ?? getReasonPhrase(this.status),\n };\n }\n\n public setHeader(key: string, value: string | string[]): void {\n setHeader(this.headers, key, value);\n }\n\n public mergeHeader(key: string, value: string | string[]): void {\n mergeHeader(this.headers, key, value);\n }\n\n public addContentType() {\n if (this.mediaType) {\n this.headers.set(HttpHeader.CONTENT_TYPE, getContentType(this.mediaType));\n }\n }\n}\n\nabstract class CorsResponse extends BaseResponse {\n constructor(public readonly cors: CorsProvider, content: BodyInit | null = null) {\n super(content);\n }\n\n protected addCorsHeaders(): void {\n addCorsHeaders(this.cors, this.headers);\n }\n}\n\nabstract class CacheResponse extends CorsResponse {\n constructor(cors: CorsProvider, body: BodyInit | null = null, public cache?: CacheControl) {\n super(cors, body);\n }\n\n protected addCacheHeaders(): void {\n if (this.cache) {\n this.headers.set(HttpHeader.CACHE_CONTROL, CacheControl.stringify(this.cache));\n }\n }\n}\n\nexport abstract class WorkerResponse extends CacheResponse {\n public createResponse(): Response {\n this.addCorsHeaders();\n this.addCacheHeaders();\n this.addSecurityHeaders();\n\n const body = this.status === StatusCodes.NO_CONTENT ? null : this.body;\n if (body) this.addContentType();\n return new Response(body, this.responseInit);\n }\n\n protected addSecurityHeaders(): void {\n this.setHeader(HttpHeader.X_CONTENT_TYPE_OPTIONS, HttpHeader.NOSNIFF);\n }\n}\n\nexport class ClonedResponse extends WorkerResponse {\n constructor(cors: CorsProvider, response: Response, cache?: CacheControl) {\n const clone = response.clone();\n super(cors, clone.body, cache);\n this.headers = new Headers(clone.headers);\n this.status = clone.status;\n this.statusText = clone.statusText;\n }\n}\n\nexport class SuccessResponse extends WorkerResponse {\n constructor(\n cors: CorsProvider,\n body: BodyInit | null = null,\n cache?: CacheControl,\n status: StatusCodes = StatusCodes.OK\n ) {\n super(cors, body, cache);\n this.status = status;\n }\n}\n\nexport class JsonResponse extends SuccessResponse {\n constructor(\n cors: CorsProvider,\n json: unknown = {},\n cache?: CacheControl,\n status: StatusCodes = StatusCodes.OK\n ) {\n super(cors, JSON.stringify(json), cache, status);\n this.mediaType = MediaType.JSON;\n }\n}\n\nexport class HtmlResponse extends SuccessResponse {\n constructor(\n cors: CorsProvider,\n body: string,\n cache?: CacheControl,\n status: StatusCodes = StatusCodes.OK\n ) {\n super(cors, body, cache, status);\n this.mediaType = MediaType.HTML;\n }\n}\n\nexport class TextResponse extends SuccessResponse {\n constructor(\n cors: CorsProvider,\n content: string,\n cache?: CacheControl,\n status: StatusCodes = StatusCodes.OK\n ) {\n super(cors, content, cache, status);\n this.mediaType = MediaType.PLAIN_TEXT;\n }\n}\n\n/**\n * Removes the body from a GET response.\n */\nexport class Head extends WorkerResponse {\n constructor(cors: CorsProvider, get: Response) {\n super(cors);\n this.headers = new Headers(get.headers);\n }\n}\n\nexport class Options extends SuccessResponse {\n constructor(cors: CorsProvider) {\n super(cors, null, undefined, StatusCodes.NO_CONTENT);\n this.setHeader(\"Allow\", this.cors.getAllowMethods());\n }\n}\n\nexport class HttpError extends JsonResponse {\n constructor(cors: CorsProvider, status: StatusCodes, protected readonly details?: string) {\n const cache: CacheControl = {\n \"no-cache\": true,\n \"no-store\": true,\n \"must-revalidate\": true,\n \"max-age\": 0,\n };\n super(cors, undefined, cache, status);\n }\n\n public get json(): ErrorJson {\n return {\n status: this.status,\n error: getReasonPhrase(this.status),\n details: this.details ?? getReasonPhrase(this.status),\n };\n }\n\n public override createResponse(): Response {\n this.body = JSON.stringify(this.json);\n return super.createResponse();\n }\n}\n\nexport class BadRequest extends HttpError {\n constructor(cors: CorsProvider, details?: string) {\n super(cors, StatusCodes.BAD_REQUEST, details);\n }\n}\n\nexport class Unauthorized extends HttpError {\n constructor(cors: CorsProvider, details?: string) {\n super(cors, StatusCodes.UNAUTHORIZED, details);\n }\n}\n\nexport class Forbidden extends HttpError {\n constructor(cors: CorsProvider, details?: string) {\n super(cors, StatusCodes.FORBIDDEN, details);\n }\n}\n\nexport class NotFound extends HttpError {\n constructor(cors: CorsProvider, details?: string) {\n super(cors, StatusCodes.NOT_FOUND, details);\n }\n}\n\nexport class MethodNotAllowed extends HttpError {\n constructor(cors: CorsProvider, method: string) {\n super(cors, StatusCodes.METHOD_NOT_ALLOWED, `${method} method not allowed.`);\n this.setHeader(\"Allow\", this.cors.getAllowMethods());\n }\n\n public override get json(): ErrorJson & { allowed: Method[] } {\n return {\n ...super.json,\n allowed: this.cors.getAllowMethods(),\n };\n }\n}\n\nexport class InternalServerError extends HttpError {\n constructor(cors: CorsProvider, details?: string) {\n super(cors, StatusCodes.INTERNAL_SERVER_ERROR, details);\n }\n}\n\nexport class NotImplemented extends HttpError {\n constructor(cors: CorsProvider, details?: string) {\n super(cors, StatusCodes.NOT_IMPLEMENTED, details);\n }\n}\n\nexport class MethodNotImplemented extends NotImplemented {\n constructor(cors: CorsProvider, method: Method) {\n super(cors, `${method} method not implemented.`);\n }\n}\n\nexport class ServiceUnavailable extends HttpError {\n constructor(cors: CorsProvider, details?: string) {\n super(cors, 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 { CacheWorker } from \"./cache-worker\";\nimport { isMethod, Method } from \"./common\";\nimport { CorsProvider } from \"./cors\";\nimport {\n Head,\n InternalServerError,\n MethodNotAllowed,\n MethodNotImplemented,\n Options,\n WorkerResponse,\n} from \"./response\";\n\nexport abstract class BasicWorker extends CacheWorker {\n public async fetch(): Promise<Response> {\n if (!this.isAllowed(this.request.method)) {\n return this.getResponse(MethodNotAllowed, this.request.method);\n }\n\n try {\n return await this.dispatch();\n } catch (error) {\n return this.getResponse(InternalServerError, String(error));\n }\n }\n\n protected async dispatch(request: Request = this.request): Promise<Response> {\n const method = request.method as Method;\n const handler: Record<Method, () => Promise<Response>> = {\n GET: () => this.get(),\n PUT: () => this.put(),\n POST: () => this.post(),\n PATCH: () => this.patch(),\n DELETE: () => this.delete(),\n HEAD: () => this.head(),\n OPTIONS: () => this.options(),\n };\n return (handler[method] ?? (() => this.getResponse(MethodNotAllowed, method)))();\n }\n\n protected async get(): Promise<Response> {\n return this.getResponse(MethodNotImplemented, Method.GET);\n }\n\n protected async put(): Promise<Response> {\n return this.getResponse(MethodNotImplemented, Method.PUT);\n }\n\n protected async post(): Promise<Response> {\n return this.getResponse(MethodNotImplemented, Method.POST);\n }\n\n protected async patch(): Promise<Response> {\n return this.getResponse(MethodNotImplemented, Method.PATCH);\n }\n\n protected async delete(): Promise<Response> {\n return this.getResponse(MethodNotImplemented, Method.DELETE);\n }\n\n protected async options(): Promise<Response> {\n return this.getResponse(Options);\n }\n\n protected async head(): Promise<Response> {\n // Dispatch a new GET request created from the HEAD request\n // and return the GET response with the body removed.\n return this.getResponse(\n Head,\n await this.dispatch(new Request(this.request, { method: Method.GET }))\n );\n }\n\n protected async getResponse<\n T extends WorkerResponse,\n Ctor extends new (cors: CorsProvider, ...args: any[]) => T\n >(\n ResponseClass: Ctor,\n ...args: ConstructorParameters<Ctor> extends [CorsProvider, ...infer R] ? R : never\n ): Promise<Response> {\n const response = new ResponseClass(this, ...args).createResponse();\n this.setCachedResponse(response);\n return response;\n }\n\n public isAllowed(method: string): boolean {\n return isMethod(method) && this.getAllowMethods().includes(method);\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 \"./common\";\n\n/**\n * A callback function for a route.\n *\n * @param matches - Captured groups from the route RegExp, or the full match at index 0\n * @returns A Response object or a Promise that resolves to a Response\n */\nexport type RouteCallback = (...matches: string[]) => Response | Promise<Response>;\n\n/**\n * Tuple used to initialize a route.\n *\n * [HTTP method, path pattern (string or RegExp), callback function]\n */\nexport type RouteInit = [Method, RegExp | string, RouteCallback];\n\n/**\n * Represents a single route with a pattern and a callback.\n */\nexport class Route {\n public readonly pattern: RegExp;\n\n /**\n * @param pattern - A RegExp or string used to match the request path\n * @param callback - Function to handle requests matching the pattern\n */\n constructor(pattern: RegExp | string, public readonly callback: RouteCallback) {\n this.pattern = new RegExp(pattern);\n }\n}\n\n/**\n * A collection of routes grouped by HTTP method.\n *\n * Supports adding routes and retrieving the first matching route for a given method and URL.\n */\nexport class Routes {\n private readonly map = new Map<Method, Route[]>();\n\n /**\n * Adds a route to the collection under the given HTTP method.\n *\n * @param method - HTTP method (GET, POST, etc.)\n * @param route - Route instance to add\n * @returns The Routes instance (for chaining)\n */\n public add(method: Method, route: Route): this {\n const existing = this.map.get(method);\n if (existing) {\n existing.push(route);\n } else {\n this.map.set(method, [route]);\n }\n return this;\n }\n\n /**\n * Finds the first route that matches the given method and URL.\n *\n * @param method - HTTP method of the request\n * @param url - Full URL string of the request\n * @returns The first matching Route, or undefined if none match\n */\n public get(method: Method, url: string): Route | undefined {\n const routes = this.map.get(method);\n if (!routes) return undefined;\n\n const pathname = new URL(url).pathname;\n return routes.find(({ pattern }) => pattern.test(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 { BasicWorker } from \"./basic-worker\";\nimport { Method } from \"./common\";\nimport { NotFound } from \"./response\";\nimport { Route, Routes, RouteInit, RouteCallback } from \"./routes\";\n\nexport abstract class RoutedWorker extends BasicWorker {\n private readonly routes: Routes = new Routes();\n\n protected initialize(routes: RouteInit[]) {\n routes.forEach(([method, pattern, callback]) => {\n this.add(method, pattern, callback);\n });\n }\n\n protected add(method: Method, pattern: RegExp | string, callback: RouteCallback) {\n this.routes.add(method, new Route(pattern, callback));\n return this;\n }\n\n protected async dispatch(request: Request = this.request): Promise<Response> {\n const route = this.routes.get(request.method as Method, request.url);\n if (!route) return super.dispatch(request);\n\n const match = new URL(request.url).pathname.match(route.pattern) ?? [];\n return route.callback.call(this, ...match);\n }\n\n protected override async get(): Promise<Response> {\n return this.getResponse(NotFound);\n }\n\n protected override async put(): Promise<Response> {\n return this.getResponse(NotFound);\n }\n\n protected override async post(): Promise<Response> {\n return this.getResponse(NotFound);\n }\n\n protected override async patch(): Promise<Response> {\n return this.getResponse(NotFound);\n }\n\n protected override async delete(): Promise<Response> {\n return this.getResponse(NotFound);\n }\n}\n"],"mappings":"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","names":["HttpHeader","Method","MediaType","Cors","StatusCodes","StatusCodes"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonix.org/cloud-spark",
3
- "version": "0.0.89",
3
+ "version": "0.0.91",
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",