@adonix.org/cloud-spark 0.0.94 → 0.0.97
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 +258 -259
- package/dist/index.js +264 -256
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -15,14 +15,15 @@ var CacheControl = {
|
|
|
15
15
|
var HttpHeader;
|
|
16
16
|
((HttpHeader2) => {
|
|
17
17
|
HttpHeader2.VARY = "Vary";
|
|
18
|
+
HttpHeader2.ALLOW = "Allow";
|
|
18
19
|
HttpHeader2.CONTENT_TYPE = "Content-Type";
|
|
19
20
|
HttpHeader2.CACHE_CONTROL = "Cache-Control";
|
|
20
|
-
HttpHeader2.X_CONTENT_TYPE_OPTIONS = "X-Content-Type-Options";
|
|
21
21
|
HttpHeader2.X_FRAME_OPTIONS = "X-Frame-Options";
|
|
22
|
-
HttpHeader2.
|
|
23
|
-
HttpHeader2.CONTENT_SECURITY_POLICY = "Content-Security-Policy";
|
|
22
|
+
HttpHeader2.X_CONTENT_TYPE_OPTIONS = "X-Content-Type-Options";
|
|
24
23
|
HttpHeader2.REFERRER_POLICY = "Referrer-Policy";
|
|
25
24
|
HttpHeader2.PERMISSIONS_POLICY = "Permissions-Policy";
|
|
25
|
+
HttpHeader2.CONTENT_SECURITY_POLICY = "Content-Security-Policy";
|
|
26
|
+
HttpHeader2.STRICT_TRANSPORT_SECURITY = "Strict-Transport-Security";
|
|
26
27
|
HttpHeader2.NOSNIFF = "nosniff";
|
|
27
28
|
HttpHeader2.ORIGIN = "Origin";
|
|
28
29
|
})(HttpHeader || (HttpHeader = {}));
|
|
@@ -43,10 +44,10 @@ var Time = {
|
|
|
43
44
|
var Method = /* @__PURE__ */ ((Method4) => {
|
|
44
45
|
Method4["GET"] = "GET";
|
|
45
46
|
Method4["PUT"] = "PUT";
|
|
47
|
+
Method4["HEAD"] = "HEAD";
|
|
46
48
|
Method4["POST"] = "POST";
|
|
47
49
|
Method4["PATCH"] = "PATCH";
|
|
48
50
|
Method4["DELETE"] = "DELETE";
|
|
49
|
-
Method4["HEAD"] = "HEAD";
|
|
50
51
|
Method4["OPTIONS"] = "OPTIONS";
|
|
51
52
|
return Method4;
|
|
52
53
|
})(Method || {});
|
|
@@ -107,22 +108,22 @@ var MediaType = /* @__PURE__ */ ((MediaType2) => {
|
|
|
107
108
|
return MediaType2;
|
|
108
109
|
})(MediaType || {});
|
|
109
110
|
var ADD_CHARSET = /* @__PURE__ */ new Set([
|
|
110
|
-
"text/plain" /* PLAIN_TEXT */,
|
|
111
|
-
"text/html" /* HTML */,
|
|
112
111
|
"text/css" /* CSS */,
|
|
113
112
|
"text/csv" /* CSV */,
|
|
114
|
-
"text/markdown" /* MARKDOWN */,
|
|
115
113
|
"text/xml" /* XML */,
|
|
114
|
+
"image/svg+xml" /* SVG */,
|
|
115
|
+
"text/html" /* HTML */,
|
|
116
116
|
"application/json" /* JSON */,
|
|
117
|
-
"application/xml" /* XML_APP */,
|
|
118
|
-
"application/x-www-form-urlencoded" /* FORM_URLENCODED */,
|
|
119
117
|
"application/x-ndjson" /* NDJSON */,
|
|
118
|
+
"application/xml" /* XML_APP */,
|
|
119
|
+
"text/markdown" /* MARKDOWN */,
|
|
120
120
|
"text/richtext" /* RICH_TEXT */,
|
|
121
|
-
"
|
|
121
|
+
"text/plain" /* PLAIN_TEXT */,
|
|
122
|
+
"application/x-www-form-urlencoded" /* FORM_URLENCODED */
|
|
122
123
|
]);
|
|
123
124
|
function setHeader(headers, key, value) {
|
|
124
125
|
const raw = Array.isArray(value) ? value : [value];
|
|
125
|
-
const values = Array.from(new Set(raw.map((v) => v.trim()))).filter((v) => v.length);
|
|
126
|
+
const values = Array.from(new Set(raw.map((v) => v.trim()))).filter((v) => v.length).sort();
|
|
126
127
|
if (!values.length) {
|
|
127
128
|
headers.delete(key);
|
|
128
129
|
return;
|
|
@@ -156,217 +157,79 @@ function getOrigin(request) {
|
|
|
156
157
|
// src/cors.ts
|
|
157
158
|
var Cors;
|
|
158
159
|
((Cors2) => {
|
|
160
|
+
Cors2.MAX_AGE = "Access-Control-Max-Age";
|
|
159
161
|
Cors2.ALLOW_ORIGIN = "Access-Control-Allow-Origin";
|
|
160
|
-
Cors2.ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
|
|
161
|
-
Cors2.EXPOSE_HEADERS = "Access-Control-Expose-Headers";
|
|
162
162
|
Cors2.ALLOW_HEADERS = "Access-Control-Allow-Headers";
|
|
163
163
|
Cors2.ALLOW_METHODS = "Access-Control-Allow-Methods";
|
|
164
|
-
Cors2.
|
|
164
|
+
Cors2.EXPOSE_HEADERS = "Access-Control-Expose-Headers";
|
|
165
|
+
Cors2.ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
|
|
165
166
|
Cors2.ALLOW_ALL_ORIGINS = "*";
|
|
166
167
|
})(Cors || (Cors = {}));
|
|
167
168
|
function addCorsHeaders(origin, cors, headers) {
|
|
168
169
|
deleteCorsHeaders(headers);
|
|
169
|
-
if (!origin) return;
|
|
170
|
+
if (!origin || origin.trim() === "") return;
|
|
170
171
|
if (cors.allowAnyOrigin()) {
|
|
171
172
|
setHeader(headers, Cors.ALLOW_ORIGIN, Cors.ALLOW_ALL_ORIGINS);
|
|
172
173
|
} else if (cors.getAllowOrigins().includes(origin)) {
|
|
173
174
|
setHeader(headers, Cors.ALLOW_ORIGIN, origin);
|
|
174
175
|
setHeader(headers, Cors.ALLOW_CREDENTIALS, "true");
|
|
175
|
-
mergeHeader(headers, HttpHeader.VARY, HttpHeader.ORIGIN);
|
|
176
176
|
}
|
|
177
|
-
mergeHeader(headers, Cors.EXPOSE_HEADERS, cors.getExposeHeaders());
|
|
178
|
-
setHeader(headers, Cors.ALLOW_HEADERS, cors.getAllowHeaders());
|
|
179
|
-
setHeader(headers, Cors.ALLOW_METHODS, cors.getAllowMethods());
|
|
180
177
|
setHeader(headers, Cors.MAX_AGE, String(cors.getMaxAge()));
|
|
178
|
+
setHeader(headers, Cors.ALLOW_METHODS, cors.getAllowMethods());
|
|
179
|
+
setHeader(headers, Cors.ALLOW_HEADERS, cors.getAllowHeaders());
|
|
180
|
+
mergeHeader(headers, Cors.EXPOSE_HEADERS, cors.getExposeHeaders());
|
|
181
181
|
}
|
|
182
182
|
function deleteCorsHeaders(headers) {
|
|
183
|
+
headers.delete(Cors.MAX_AGE);
|
|
183
184
|
headers.delete(Cors.ALLOW_ORIGIN);
|
|
184
|
-
headers.delete(Cors.
|
|
185
|
-
headers.delete(Cors.EXPOSE_HEADERS);
|
|
185
|
+
headers.delete(Cors.ALLOW_HEADERS);
|
|
186
186
|
headers.delete(Cors.ALLOW_METHODS);
|
|
187
|
-
headers.delete(Cors.
|
|
187
|
+
headers.delete(Cors.EXPOSE_HEADERS);
|
|
188
|
+
headers.delete(Cors.ALLOW_CREDENTIALS);
|
|
188
189
|
}
|
|
189
190
|
|
|
190
|
-
// src/
|
|
191
|
-
var
|
|
192
|
-
constructor(_request, _env, _ctx) {
|
|
193
|
-
this._request = _request;
|
|
194
|
-
this._env = _env;
|
|
195
|
-
this._ctx = _ctx;
|
|
196
|
-
}
|
|
197
|
-
/** The Request object associated with this worker invocation */
|
|
198
|
-
get request() {
|
|
199
|
-
return this._request;
|
|
200
|
-
}
|
|
201
|
-
/** Environment bindings (e.g., KV, secrets, or other globals) */
|
|
202
|
-
get env() {
|
|
203
|
-
return this._env;
|
|
204
|
-
}
|
|
205
|
-
/** Optional execution context for background tasks or `waitUntil` */
|
|
206
|
-
get ctx() {
|
|
207
|
-
return this._ctx;
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Creates a new instance of the current Worker subclass.
|
|
211
|
-
*
|
|
212
|
-
* @param request - The request to pass to the new worker instance.
|
|
213
|
-
* @returns A new worker instance of the same subclass as `this`.
|
|
214
|
-
*/
|
|
215
|
-
createWorker(request) {
|
|
216
|
-
const ctor = this.constructor;
|
|
217
|
-
return new ctor(request, this.env, this.ctx);
|
|
218
|
-
}
|
|
191
|
+
// src/routes.ts
|
|
192
|
+
var Route = class {
|
|
219
193
|
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
* @returns A `FetchHandler` that launches a new worker instance for each request.
|
|
223
|
-
*
|
|
224
|
-
* @example
|
|
225
|
-
* ```ts
|
|
226
|
-
* export default MyWorker.ignite();
|
|
227
|
-
* ```
|
|
194
|
+
* @param pattern - A RegExp or string used to match the request path
|
|
195
|
+
* @param callback - Function to handle requests matching the pattern
|
|
228
196
|
*/
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
// src/cors-worker.ts
|
|
237
|
-
var CorsWorker = class extends BaseWorker {
|
|
238
|
-
getAllowOrigins() {
|
|
239
|
-
return ["*"];
|
|
240
|
-
}
|
|
241
|
-
allowAnyOrigin() {
|
|
242
|
-
return this.getAllowOrigins().includes("*");
|
|
243
|
-
}
|
|
244
|
-
getAllowMethods() {
|
|
245
|
-
return ["GET" /* GET */, "OPTIONS" /* OPTIONS */, "HEAD" /* HEAD */];
|
|
246
|
-
}
|
|
247
|
-
getAllowHeaders() {
|
|
248
|
-
return ["Content-Type"];
|
|
249
|
-
}
|
|
250
|
-
getExposeHeaders() {
|
|
251
|
-
return [];
|
|
252
|
-
}
|
|
253
|
-
getMaxAge() {
|
|
254
|
-
return Time.Week;
|
|
197
|
+
constructor(pattern, callback) {
|
|
198
|
+
this.callback = callback;
|
|
199
|
+
this.pattern = new RegExp(pattern);
|
|
255
200
|
}
|
|
201
|
+
pattern;
|
|
256
202
|
};
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
var CacheWorker = class extends CorsWorker {
|
|
260
|
-
/**
|
|
261
|
-
* Returns the cache key for the current request.
|
|
262
|
-
*
|
|
263
|
-
* Behavior:
|
|
264
|
-
* - By default, returns the normalized request URL.
|
|
265
|
-
* - Query parameters are normalized so that the order does not affect the cache key.
|
|
266
|
-
* For example, `?a=1&b=2` and `?b=2&a=1` produce the same cache key.
|
|
267
|
-
*
|
|
268
|
-
* Subclasses may override this method to implement custom cache key strategies.
|
|
269
|
-
*
|
|
270
|
-
* @returns {URL | RequestInfo} The URL or RequestInfo used as the cache key.
|
|
271
|
-
*/
|
|
272
|
-
getCacheKey() {
|
|
273
|
-
return normalizeUrl(this.request.url);
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* Retrieves a cached Response for the current request, if one exists.
|
|
277
|
-
*
|
|
278
|
-
* Behavior:
|
|
279
|
-
* - Only GET requests are considered.
|
|
280
|
-
* - Returns a new Response with dynamic CORS headers applied via `addCacheHeaders`.
|
|
281
|
-
* - Returns `undefined` if no cached response is found.
|
|
282
|
-
* - Cloudflare dynamic headers (`CF-Cache-Status`, `Age`, `Connection`, etc.) will
|
|
283
|
-
* always be present on the returned response, even though they are not stored in the cache.
|
|
284
|
-
*
|
|
285
|
-
* @param {string} [cacheName] Optional named cache; defaults to `caches.default`.
|
|
286
|
-
* @returns {Promise<Response | undefined>} A Response with CORS headers, or undefined.
|
|
287
|
-
* @see {@link setCachedResponse}
|
|
288
|
-
* @see {@link getCacheKey}
|
|
289
|
-
*/
|
|
290
|
-
async getCachedResponse(cacheName) {
|
|
291
|
-
if (this.request.method !== "GET" /* GET */) return;
|
|
292
|
-
const cache = cacheName ? await caches.open(cacheName) : caches.default;
|
|
293
|
-
const response = await cache.match(this.getCacheKey());
|
|
294
|
-
return response ? this.addCacheHeaders(response) : void 0;
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* Stores a Response in the cache for the current request.
|
|
298
|
-
*
|
|
299
|
-
* Behavior:
|
|
300
|
-
* - Only caches successful GET responses (`response.ok === true`).
|
|
301
|
-
* - Strips headers via `removeCacheHeaders` before storing.
|
|
302
|
-
* - Uses `ctx.waitUntil` to perform caching asynchronously without blocking the response.
|
|
303
|
-
* - All other origin headers (e.g., Cache-Control, Expires) are preserved.
|
|
304
|
-
*
|
|
305
|
-
* @param {Response} response The Response to cache.
|
|
306
|
-
* @param {string} [cacheName] Optional named cache; defaults to `caches.default`.
|
|
307
|
-
* @see {@link getCachedResponse}
|
|
308
|
-
* @see {@link getCacheKey}
|
|
309
|
-
*/
|
|
310
|
-
async setCachedResponse(response, cacheName) {
|
|
311
|
-
if (!response.ok) return;
|
|
312
|
-
if (this.request.method !== "GET" /* GET */) return;
|
|
313
|
-
const cache = cacheName ? await caches.open(cacheName) : caches.default;
|
|
314
|
-
this.ctx.waitUntil(
|
|
315
|
-
cache.put(this.getCacheKey(), this.removeCacheHeaders(response.clone()))
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Adds headers to a cached response.
|
|
320
|
-
*
|
|
321
|
-
* @param {Response} cached The cached Response.
|
|
322
|
-
* @returns {Response} A new Response with dynamic CORS headers applied.
|
|
323
|
-
* @see {@link removeCacheHeaders}
|
|
324
|
-
*/
|
|
325
|
-
addCacheHeaders(cached) {
|
|
326
|
-
const headers = new Headers(cached.headers);
|
|
327
|
-
addCorsHeaders(getOrigin(this.request), this, headers);
|
|
328
|
-
return new Response(cached.body, {
|
|
329
|
-
status: cached.status,
|
|
330
|
-
statusText: cached.statusText,
|
|
331
|
-
headers
|
|
332
|
-
});
|
|
333
|
-
}
|
|
203
|
+
var Routes = class {
|
|
204
|
+
map = /* @__PURE__ */ new Map();
|
|
334
205
|
/**
|
|
335
|
-
*
|
|
206
|
+
* Adds a route to the collection under the given HTTP method.
|
|
336
207
|
*
|
|
337
|
-
* @param
|
|
338
|
-
* @
|
|
339
|
-
* @
|
|
208
|
+
* @param method - HTTP method (GET, POST, etc.)
|
|
209
|
+
* @param route - Route instance to add
|
|
210
|
+
* @returns The Routes instance (for chaining)
|
|
340
211
|
*/
|
|
341
|
-
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
212
|
+
add(method, route) {
|
|
213
|
+
const existing = this.map.get(method);
|
|
214
|
+
if (existing) {
|
|
215
|
+
existing.push(route);
|
|
216
|
+
} else {
|
|
217
|
+
this.map.set(method, [route]);
|
|
348
218
|
}
|
|
349
|
-
return
|
|
350
|
-
status: response.status,
|
|
351
|
-
statusText: response.statusText,
|
|
352
|
-
headers
|
|
353
|
-
});
|
|
219
|
+
return this;
|
|
354
220
|
}
|
|
355
221
|
/**
|
|
356
|
-
*
|
|
357
|
-
* By default, excludes only dynamic CORS headers.
|
|
222
|
+
* Finds the first route that matches the given method and URL.
|
|
358
223
|
*
|
|
359
|
-
* @
|
|
360
|
-
* @
|
|
224
|
+
* @param method - HTTP method of the request
|
|
225
|
+
* @param url - Full URL string of the request
|
|
226
|
+
* @returns The first matching Route, or undefined if none match
|
|
361
227
|
*/
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
Cors.ALLOW_METHODS,
|
|
368
|
-
Cors.MAX_AGE
|
|
369
|
-
];
|
|
228
|
+
get(method, url) {
|
|
229
|
+
const routes = this.map.get(method);
|
|
230
|
+
if (!routes) return void 0;
|
|
231
|
+
const pathname = new URL(url).pathname;
|
|
232
|
+
return routes.find(({ pattern }) => pattern.test(pathname));
|
|
370
233
|
}
|
|
371
234
|
};
|
|
372
235
|
|
|
@@ -407,6 +270,9 @@ var CorsResponse = class extends BaseResponse {
|
|
|
407
270
|
}
|
|
408
271
|
addCorsHeaders() {
|
|
409
272
|
addCorsHeaders(this.getOrigin(), this.worker, this.headers);
|
|
273
|
+
if (!this.worker.allowAnyOrigin()) {
|
|
274
|
+
mergeHeader(this.headers, HttpHeader.VARY, HttpHeader.ORIGIN);
|
|
275
|
+
}
|
|
410
276
|
}
|
|
411
277
|
getOrigin() {
|
|
412
278
|
return getOrigin(this.worker.request);
|
|
@@ -478,7 +344,7 @@ var Head = class extends WorkerResponse {
|
|
|
478
344
|
var Options = class extends SuccessResponse {
|
|
479
345
|
constructor(worker) {
|
|
480
346
|
super(worker, null, void 0, StatusCodes2.NO_CONTENT);
|
|
481
|
-
this.setHeader(
|
|
347
|
+
this.setHeader(HttpHeader.ALLOW, this.worker.getAllowMethods());
|
|
482
348
|
}
|
|
483
349
|
};
|
|
484
350
|
var HttpError = class extends JsonResponse {
|
|
@@ -519,9 +385,13 @@ var NotFound = class extends HttpError {
|
|
|
519
385
|
}
|
|
520
386
|
};
|
|
521
387
|
var MethodNotAllowed = class extends HttpError {
|
|
522
|
-
constructor(worker
|
|
523
|
-
super(
|
|
524
|
-
|
|
388
|
+
constructor(worker) {
|
|
389
|
+
super(
|
|
390
|
+
worker,
|
|
391
|
+
StatusCodes2.METHOD_NOT_ALLOWED,
|
|
392
|
+
`${worker.request.method} method not allowed.`
|
|
393
|
+
);
|
|
394
|
+
this.setHeader(HttpHeader.ALLOW, this.worker.getAllowMethods());
|
|
525
395
|
}
|
|
526
396
|
get json() {
|
|
527
397
|
return {
|
|
@@ -541,8 +411,8 @@ var NotImplemented = class extends HttpError {
|
|
|
541
411
|
}
|
|
542
412
|
};
|
|
543
413
|
var MethodNotImplemented = class extends NotImplemented {
|
|
544
|
-
constructor(worker
|
|
545
|
-
super(worker, `${method} method not implemented.`);
|
|
414
|
+
constructor(worker) {
|
|
415
|
+
super(worker, `${worker.request.method} method not implemented.`);
|
|
546
416
|
}
|
|
547
417
|
};
|
|
548
418
|
var ServiceUnavailable = class extends HttpError {
|
|
@@ -551,11 +421,194 @@ var ServiceUnavailable = class extends HttpError {
|
|
|
551
421
|
}
|
|
552
422
|
};
|
|
553
423
|
|
|
424
|
+
// src/base-worker.ts
|
|
425
|
+
var BaseWorker = class {
|
|
426
|
+
constructor(_request, _env, _ctx) {
|
|
427
|
+
this._request = _request;
|
|
428
|
+
this._env = _env;
|
|
429
|
+
this._ctx = _ctx;
|
|
430
|
+
}
|
|
431
|
+
/** The Request object associated with this worker invocation */
|
|
432
|
+
get request() {
|
|
433
|
+
return this._request;
|
|
434
|
+
}
|
|
435
|
+
/** Environment bindings (e.g., KV, secrets, or other globals) */
|
|
436
|
+
get env() {
|
|
437
|
+
return this._env;
|
|
438
|
+
}
|
|
439
|
+
/** Execution context for background tasks or `waitUntil` */
|
|
440
|
+
get ctx() {
|
|
441
|
+
return this._ctx;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Creates a new instance of the current Worker subclass.
|
|
445
|
+
*
|
|
446
|
+
* @param request - The {@link Request} to pass to the new worker instance.
|
|
447
|
+
* @returns A new worker instance of the same subclass as `this`.
|
|
448
|
+
*/
|
|
449
|
+
createWorker(request) {
|
|
450
|
+
const ctor = this.constructor;
|
|
451
|
+
return new ctor(request, this.env, this.ctx);
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* **Ignite** your `Worker` implementation into a Cloudflare-compatible handler.
|
|
455
|
+
*
|
|
456
|
+
* @returns A `FetchHandler` that launches a new worker instance for each request.
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* ```ts
|
|
460
|
+
* export default MyWorker.ignite();
|
|
461
|
+
* ```
|
|
462
|
+
*/
|
|
463
|
+
static ignite() {
|
|
464
|
+
return {
|
|
465
|
+
fetch: (req, env, ctx) => new this(req, env, ctx).fetch()
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
// src/cors-worker.ts
|
|
471
|
+
var CorsWorker = class extends BaseWorker {
|
|
472
|
+
getAllowOrigins() {
|
|
473
|
+
return ["*"];
|
|
474
|
+
}
|
|
475
|
+
allowAnyOrigin() {
|
|
476
|
+
return this.getAllowOrigins().includes("*");
|
|
477
|
+
}
|
|
478
|
+
getAllowMethods() {
|
|
479
|
+
return ["GET" /* GET */, "HEAD" /* HEAD */, "OPTIONS" /* OPTIONS */];
|
|
480
|
+
}
|
|
481
|
+
getAllowHeaders() {
|
|
482
|
+
return ["Content-Type"];
|
|
483
|
+
}
|
|
484
|
+
getExposeHeaders() {
|
|
485
|
+
return [];
|
|
486
|
+
}
|
|
487
|
+
getMaxAge() {
|
|
488
|
+
return Time.Week;
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
// src/cache-worker.ts
|
|
493
|
+
var CacheWorker = class extends CorsWorker {
|
|
494
|
+
/**
|
|
495
|
+
* Returns the cache key for the current request.
|
|
496
|
+
*
|
|
497
|
+
* Behavior:
|
|
498
|
+
* - By default, returns the normalized request URL.
|
|
499
|
+
* - Query parameters are normalized so that the order does not affect the cache key.
|
|
500
|
+
* For example, `?a=1&b=2` and `?b=2&a=1` produce the same cache key.
|
|
501
|
+
*
|
|
502
|
+
* Subclasses may override this method to implement custom cache key strategies.
|
|
503
|
+
*
|
|
504
|
+
* @returns {URL | RequestInfo} The URL or RequestInfo used as the cache key.
|
|
505
|
+
*/
|
|
506
|
+
getCacheKey() {
|
|
507
|
+
return normalizeUrl(this.request.url);
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Retrieves a cached Response for the current request, if one exists.
|
|
511
|
+
*
|
|
512
|
+
* Behavior:
|
|
513
|
+
* - Only GET requests are considered.
|
|
514
|
+
* - Returns a new Response with dynamic CORS headers applied via `addCacheHeaders`.
|
|
515
|
+
* - Returns `undefined` if no cached response is found.
|
|
516
|
+
* - Cloudflare dynamic headers (`CF-Cache-Status`, `Age`, `Connection`, etc.) will
|
|
517
|
+
* always be present on the returned response, even though they are not stored in the cache.
|
|
518
|
+
*
|
|
519
|
+
* @param {string} [cacheName] Optional named cache; defaults to `caches.default`.
|
|
520
|
+
* @returns {Promise<Response | undefined>} A Response with CORS headers, or undefined.
|
|
521
|
+
* @see {@link setCachedResponse}
|
|
522
|
+
* @see {@link getCacheKey}
|
|
523
|
+
*/
|
|
524
|
+
async getCachedResponse(cacheName) {
|
|
525
|
+
if (this.request.method !== "GET" /* GET */) return;
|
|
526
|
+
const cache = cacheName ? await caches.open(cacheName) : caches.default;
|
|
527
|
+
const response = await cache.match(this.getCacheKey());
|
|
528
|
+
return response ? this.addCacheHeaders(response) : void 0;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Stores a Response in the cache for the current request.
|
|
532
|
+
*
|
|
533
|
+
* Behavior:
|
|
534
|
+
* - Only caches successful GET responses (`response.ok === true`).
|
|
535
|
+
* - Strips headers via `removeCacheHeaders` before storing.
|
|
536
|
+
* - Uses `ctx.waitUntil` to perform caching asynchronously without blocking the response.
|
|
537
|
+
* - All other origin headers (e.g., Cache-Control, Expires) are preserved.
|
|
538
|
+
*
|
|
539
|
+
* @param {Response} response The Response to cache.
|
|
540
|
+
* @param {string} [cacheName] Optional named cache; defaults to `caches.default`.
|
|
541
|
+
* @see {@link getCachedResponse}
|
|
542
|
+
* @see {@link getCacheKey}
|
|
543
|
+
*/
|
|
544
|
+
async setCachedResponse(response, cacheName) {
|
|
545
|
+
if (!response.ok) return;
|
|
546
|
+
if (this.request.method !== "GET" /* GET */) return;
|
|
547
|
+
const cache = cacheName ? await caches.open(cacheName) : caches.default;
|
|
548
|
+
this.ctx.waitUntil(
|
|
549
|
+
cache.put(this.getCacheKey(), this.removeCacheHeaders(response.clone()))
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Adds headers to a cached response.
|
|
554
|
+
*
|
|
555
|
+
* @param {Response} cached The cached Response.
|
|
556
|
+
* @returns {Response} A new Response with dynamic CORS headers applied.
|
|
557
|
+
* @see {@link removeCacheHeaders}
|
|
558
|
+
*/
|
|
559
|
+
addCacheHeaders(cached) {
|
|
560
|
+
const headers = new Headers(cached.headers);
|
|
561
|
+
addCorsHeaders(getOrigin(this.request), this, headers);
|
|
562
|
+
return new Response(cached.body, {
|
|
563
|
+
status: cached.status,
|
|
564
|
+
statusText: cached.statusText,
|
|
565
|
+
headers
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Removes headers that should not be stored in the cache (currently only CORS headers).
|
|
570
|
+
*
|
|
571
|
+
* @param {Response} response The Response to clean before caching.
|
|
572
|
+
* @returns {Response} A new Response with excluded headers removed.
|
|
573
|
+
* @see {@link addCacheHeaders}
|
|
574
|
+
*/
|
|
575
|
+
removeCacheHeaders(response) {
|
|
576
|
+
const excludeSet = new Set(this.excludeCacheHeaders().map((h) => h.toLowerCase()));
|
|
577
|
+
const headers = new Headers();
|
|
578
|
+
for (const [key, value] of response.headers) {
|
|
579
|
+
if (!excludeSet.has(key)) {
|
|
580
|
+
headers.set(key, value);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return new Response(response.body, {
|
|
584
|
+
status: response.status,
|
|
585
|
+
statusText: response.statusText,
|
|
586
|
+
headers
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Returns the list of headers to exclude from the cached response.
|
|
591
|
+
* By default, excludes only dynamic CORS headers.
|
|
592
|
+
*
|
|
593
|
+
* @returns {string[]} Array of header names to exclude.
|
|
594
|
+
* @see {@link removeCacheHeaders}
|
|
595
|
+
*/
|
|
596
|
+
excludeCacheHeaders() {
|
|
597
|
+
return [
|
|
598
|
+
Cors.ALLOW_ORIGIN,
|
|
599
|
+
Cors.ALLOW_CREDENTIALS,
|
|
600
|
+
Cors.EXPOSE_HEADERS,
|
|
601
|
+
Cors.ALLOW_METHODS,
|
|
602
|
+
Cors.MAX_AGE
|
|
603
|
+
];
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
|
|
554
607
|
// src/basic-worker.ts
|
|
555
608
|
var BasicWorker = class extends CacheWorker {
|
|
556
609
|
async fetch() {
|
|
557
610
|
if (!this.isAllowed(this.request.method)) {
|
|
558
|
-
return this.getResponse(MethodNotAllowed
|
|
611
|
+
return this.getResponse(MethodNotAllowed);
|
|
559
612
|
}
|
|
560
613
|
try {
|
|
561
614
|
return await this.dispatch();
|
|
@@ -568,34 +621,37 @@ var BasicWorker = class extends CacheWorker {
|
|
|
568
621
|
const handler = {
|
|
569
622
|
GET: () => this.get(),
|
|
570
623
|
PUT: () => this.put(),
|
|
624
|
+
HEAD: () => this.head(),
|
|
571
625
|
POST: () => this.post(),
|
|
572
626
|
PATCH: () => this.patch(),
|
|
573
627
|
DELETE: () => this.delete(),
|
|
574
|
-
HEAD: () => this.head(),
|
|
575
628
|
OPTIONS: () => this.options()
|
|
576
629
|
};
|
|
577
|
-
return (handler[method] ?? (() => this.getResponse(MethodNotAllowed
|
|
630
|
+
return (handler[method] ?? (() => this.getResponse(MethodNotAllowed)))();
|
|
631
|
+
}
|
|
632
|
+
isAllowed(method) {
|
|
633
|
+
return isMethod(method) && this.getAllowMethods().includes(method);
|
|
578
634
|
}
|
|
579
635
|
async get() {
|
|
580
|
-
return this.getResponse(MethodNotImplemented
|
|
636
|
+
return this.getResponse(MethodNotImplemented);
|
|
581
637
|
}
|
|
582
638
|
async put() {
|
|
583
|
-
return this.getResponse(MethodNotImplemented
|
|
639
|
+
return this.getResponse(MethodNotImplemented);
|
|
584
640
|
}
|
|
585
641
|
async post() {
|
|
586
|
-
return this.getResponse(MethodNotImplemented
|
|
642
|
+
return this.getResponse(MethodNotImplemented);
|
|
587
643
|
}
|
|
588
644
|
async patch() {
|
|
589
|
-
return this.getResponse(MethodNotImplemented
|
|
645
|
+
return this.getResponse(MethodNotImplemented);
|
|
590
646
|
}
|
|
591
647
|
async delete() {
|
|
592
|
-
return this.getResponse(MethodNotImplemented
|
|
648
|
+
return this.getResponse(MethodNotImplemented);
|
|
593
649
|
}
|
|
594
650
|
async options() {
|
|
595
651
|
return this.getResponse(Options);
|
|
596
652
|
}
|
|
597
653
|
async head() {
|
|
598
|
-
const worker = this.createWorker(new Request(this.request, { method: "GET" }));
|
|
654
|
+
const worker = this.createWorker(new Request(this.request, { method: "GET" /* GET */ }));
|
|
599
655
|
return this.getResponse(Head, await worker.fetch());
|
|
600
656
|
}
|
|
601
657
|
async getResponse(ResponseClass, ...args) {
|
|
@@ -603,58 +659,10 @@ var BasicWorker = class extends CacheWorker {
|
|
|
603
659
|
this.setCachedResponse(response);
|
|
604
660
|
return response;
|
|
605
661
|
}
|
|
606
|
-
isAllowed(method) {
|
|
607
|
-
return isMethod(method) && this.getAllowMethods().includes(method);
|
|
608
|
-
}
|
|
609
|
-
};
|
|
610
|
-
|
|
611
|
-
// src/routes.ts
|
|
612
|
-
var Route = class {
|
|
613
|
-
/**
|
|
614
|
-
* @param pattern - A RegExp or string used to match the request path
|
|
615
|
-
* @param callback - Function to handle requests matching the pattern
|
|
616
|
-
*/
|
|
617
|
-
constructor(pattern, callback) {
|
|
618
|
-
this.callback = callback;
|
|
619
|
-
this.pattern = new RegExp(pattern);
|
|
620
|
-
}
|
|
621
|
-
pattern;
|
|
622
|
-
};
|
|
623
|
-
var Routes = class {
|
|
624
|
-
map = /* @__PURE__ */ new Map();
|
|
625
|
-
/**
|
|
626
|
-
* Adds a route to the collection under the given HTTP method.
|
|
627
|
-
*
|
|
628
|
-
* @param method - HTTP method (GET, POST, etc.)
|
|
629
|
-
* @param route - Route instance to add
|
|
630
|
-
* @returns The Routes instance (for chaining)
|
|
631
|
-
*/
|
|
632
|
-
add(method, route) {
|
|
633
|
-
const existing = this.map.get(method);
|
|
634
|
-
if (existing) {
|
|
635
|
-
existing.push(route);
|
|
636
|
-
} else {
|
|
637
|
-
this.map.set(method, [route]);
|
|
638
|
-
}
|
|
639
|
-
return this;
|
|
640
|
-
}
|
|
641
|
-
/**
|
|
642
|
-
* Finds the first route that matches the given method and URL.
|
|
643
|
-
*
|
|
644
|
-
* @param method - HTTP method of the request
|
|
645
|
-
* @param url - Full URL string of the request
|
|
646
|
-
* @returns The first matching Route, or undefined if none match
|
|
647
|
-
*/
|
|
648
|
-
get(method, url) {
|
|
649
|
-
const routes = this.map.get(method);
|
|
650
|
-
if (!routes) return void 0;
|
|
651
|
-
const pathname = new URL(url).pathname;
|
|
652
|
-
return routes.find(({ pattern }) => pattern.test(pathname));
|
|
653
|
-
}
|
|
654
662
|
};
|
|
655
663
|
|
|
656
|
-
// src/
|
|
657
|
-
var
|
|
664
|
+
// src/route-worker.ts
|
|
665
|
+
var RouteWorker = class extends BasicWorker {
|
|
658
666
|
routes = new Routes();
|
|
659
667
|
initialize(routes) {
|
|
660
668
|
routes.forEach(([method, pattern, callback]) => {
|
|
@@ -708,7 +716,7 @@ export {
|
|
|
708
716
|
NotImplemented,
|
|
709
717
|
Options,
|
|
710
718
|
Route,
|
|
711
|
-
|
|
719
|
+
RouteWorker,
|
|
712
720
|
Routes,
|
|
713
721
|
ServiceUnavailable,
|
|
714
722
|
StatusCodes,
|