UNPKG

38.9 kBJavaScriptView Raw
1"use strict";
2var __defProp = Object.defineProperty;
3var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4var __getOwnPropNames = Object.getOwnPropertyNames;
5var __hasOwnProp = Object.prototype.hasOwnProperty;
6var __export = (target, all) => {
7 for (var name2 in all)
8 __defProp(target, name2, { get: all[name2], enumerable: true });
9};
10var __copyProps = (to, from, except, desc) => {
11 if (from && typeof from === "object" || typeof from === "function") {
12 for (let key of __getOwnPropNames(from))
13 if (!__hasOwnProp.call(to, key) && key !== except)
14 __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15 }
16 return to;
17};
18var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
20// src/index.ts
21var src_exports = {};
22__export(src_exports, {
23 ALL: () => ALL,
24 BASE_URL: () => BASE_URL,
25 CONNECT: () => CONNECT,
26 Cookies: () => Cookies,
27 DELETE: () => DELETE,
28 Data: () => Data,
29 GET: () => GET,
30 HEAD: () => HEAD,
31 Method: () => Method,
32 OPTIONS: () => OPTIONS,
33 PATCH: () => PATCH,
34 POST: () => POST,
35 PUT: () => PUT,
36 Reply: () => Reply,
37 Router: () => Router,
38 SameSite: () => SameSite,
39 Server: () => Server,
40 Status: () => Status,
41 TRACE: () => TRACE,
42 compareCompatibilityDates: () => compareCompatibilityDates,
43 compareMethods: () => compareMethods,
44 comparePathnames: () => comparePathnames,
45 cors: () => cors,
46 defaultCookiesOptions: () => defaultCookiesOptions,
47 getPathnameParameters: () => getPathnameParameters,
48 isValidCompatibilityDate: () => isValidCompatibilityDate,
49 isValidPathname: () => isValidPathname,
50 logger: () => logger,
51 middleware: () => middleware,
52 next: () => next,
53 preflightHandler: () => preflightHandler,
54 recordException: () => recordException,
55 schemaValidation: () => schemaValidation,
56 server: () => server
57});
58module.exports = __toCommonJS(src_exports);
59
60// src/logger.ts
61var import_api = require("@opentelemetry/api");
62var import_api_logs = require("@opentelemetry/api-logs");
63
64// package.json
65var name = "@neoaren/comet";
66var version = "3.4.1";
67
68// src/logger.ts
69var otelLogger = import_api_logs.logs.getLogger(name, version);
70function convert(...data) {
71 return data.map((entry) => {
72 if (typeof entry === "string") return entry;
73 if (entry === void 0) return "undefined";
74 return JSON.stringify(entry, null, 2);
75 }).join(", ");
76}
77var logger = {
78 trace: (...body) => otelLogger.emit({ severityNumber: import_api_logs.SeverityNumber.TRACE, body: convert(body) }),
79 debug: (...body) => otelLogger.emit({ severityNumber: import_api_logs.SeverityNumber.DEBUG, body: convert(body) }),
80 info: (...body) => otelLogger.emit({ severityNumber: import_api_logs.SeverityNumber.INFO, body: convert(body) }),
81 log: (...body) => otelLogger.emit({ severityNumber: import_api_logs.SeverityNumber.INFO, body: convert(body) }),
82 warn: (...body) => otelLogger.emit({ severityNumber: import_api_logs.SeverityNumber.WARN, body: convert(body) }),
83 error: (...body) => otelLogger.emit({ severityNumber: import_api_logs.SeverityNumber.ERROR, body: convert(body) })
84};
85function recordException(exception) {
86 if (exception instanceof Error || typeof exception === "string") {
87 import_api.trace.getActiveSpan()?.recordException(exception);
88 } else if (typeof exception === "object" && exception !== null && "toString" in exception) {
89 import_api.trace.getActiveSpan()?.recordException(exception.toString());
90 }
91}
92
93// src/cookies.ts
94var defaultCookiesOptions = {
95 decode: decodeURIComponent,
96 encode: encodeURIComponent,
97 limit: 64
98};
99var SameSite = /* @__PURE__ */ ((SameSite2) => {
100 SameSite2["Strict"] = "Strict";
101 SameSite2["Lax"] = "Lax";
102 SameSite2["None"] = "None";
103 return SameSite2;
104})(SameSite || {});
105var Cookies = class _Cookies {
106 data = /* @__PURE__ */ new Map();
107 get [Symbol.toStringTag]() {
108 return `Cookies(${this.data.size})`;
109 }
110 // Get a cookie by name
111 get(name2) {
112 return this.data.get(name2)?.value;
113 }
114 // Check if a cookie exists
115 has(name2) {
116 return this.data.has(name2);
117 }
118 // Set a cookie with a name, a value and optional metadata
119 set(name2, value, meta) {
120 this.data.set(name2, { name: name2, value, ...meta ? { meta } : {} });
121 }
122 // Delete a cookie
123 delete(name2) {
124 this.data.delete(name2);
125 }
126 // Returns an array that contains the key-value pairs for each cookie
127 entries() {
128 return [...this.data.entries()].map(([name2, cookie]) => [name2, cookie.value]);
129 }
130 // Returns an array that contains the keys for each cookie
131 keys() {
132 return [...this.data.keys()];
133 }
134 // Returns an array that contains the values for each cookie
135 values() {
136 return [...this.data.values()].map((cookie) => cookie.value);
137 }
138 // Parse cookies from headers
139 static async parse(headers, options) {
140 const allOptions = this.getAllOptions(options);
141 const cookies = new _Cookies();
142 const pairs = headers.get("Cookie")?.split(";", allOptions.limit) ?? [];
143 for (const pair of pairs) {
144 let [name2, value] = pair.split("=", 2).map((component) => component.trim());
145 if (!name2 || !value) {
146 recordException(`[Comet] Failed to parse malformatted cookie "${pair}".`);
147 continue;
148 }
149 if (value.startsWith('"') && value.endsWith('"')) value = value.slice(1, -1);
150 try {
151 if (allOptions.decode !== null) name2 = await allOptions.decode(name2);
152 if (allOptions.decode !== null) value = await allOptions.decode(value);
153 cookies.set(name2, value);
154 } catch (error) {
155 recordException(`[Comet] Failed to decode cookie "${pair}".`);
156 recordException(error);
157 }
158 }
159 return cookies;
160 }
161 // Serialize cookies to headers
162 static async serialize(cookies, headers, options) {
163 const allOptions = this.getAllOptions(options);
164 for (const cookie of cookies.data.values()) {
165 const serialized = [];
166 try {
167 const name2 = allOptions.encode === null ? cookie.name : await allOptions.encode(cookie.name);
168 const value = allOptions.encode === null ? cookie.value : await allOptions.encode(cookie.value);
169 serialized.push(`${name2}=${value}`);
170 } catch (error) {
171 recordException(`[Comet] Failed to encode cookie "${cookie.name}".`);
172 recordException(error);
173 continue;
174 }
175 if (cookie.meta?.domain) serialized.push(`Domain=${cookie.meta.domain}`);
176 if (cookie.meta?.expires) serialized.push(`Expires=${new Date(cookie.meta.expires).toUTCString()}`);
177 if (cookie.meta?.httpOnly) serialized.push("HttpOnly");
178 if (cookie.meta?.maxAge) serialized.push(`Max-Age=${cookie.meta.maxAge}`);
179 if (cookie.meta?.path) serialized.push(`Path=${cookie.meta.path}`);
180 if (cookie.meta?.sameSite) serialized.push(`SameSite=${cookie.meta.sameSite}`);
181 if (cookie.meta?.secure) serialized.push("Secure");
182 headers.append("Set-Cookie", serialized.join("; "));
183 }
184 }
185 // Get all options with fallback to default options
186 static getAllOptions(options) {
187 return { ...defaultCookiesOptions, ...options };
188 }
189};
190
191// src/middleware.ts
192var NextData = class {
193 // @ts-expect-error data could use better typing
194 constructor(data = {}) {
195 this.data = data;
196 }
197};
198var next = (extension) => new NextData(extension);
199function middleware(options, handler) {
200 const _options = typeof options === "object" ? options : {};
201 const _handler = typeof options === "function" ? options : handler;
202 if (!_handler) throw new Error("[Comet] A middleware received no handler argument.");
203 return {
204 ..._options,
205 handler: async (input) => {
206 const nextData = await _handler(input);
207 if (nextData instanceof NextData) Object.assign(input.event, nextData.data);
208 }
209 };
210}
211
212// src/types.ts
213var Method = /* @__PURE__ */ ((Method2) => {
214 Method2["ALL"] = "ALL";
215 Method2["GET"] = "GET";
216 Method2["HEAD"] = "HEAD";
217 Method2["POST"] = "POST";
218 Method2["PUT"] = "PUT";
219 Method2["DELETE"] = "DELETE";
220 Method2["CONNECT"] = "CONNECT";
221 Method2["OPTIONS"] = "OPTIONS";
222 Method2["TRACE"] = "TRACE";
223 Method2["PATCH"] = "PATCH";
224 return Method2;
225})(Method || {});
226var ALL = "ALL" /* ALL */;
227var GET = "GET" /* GET */;
228var HEAD = "HEAD" /* HEAD */;
229var POST = "POST" /* POST */;
230var PUT = "PUT" /* PUT */;
231var DELETE = "DELETE" /* DELETE */;
232var CONNECT = "CONNECT" /* CONNECT */;
233var OPTIONS = "OPTIONS" /* OPTIONS */;
234var TRACE = "TRACE" /* TRACE */;
235var PATCH = "PATCH" /* PATCH */;
236
237// src/cors.ts
238var defaultCorsOptions = {
239 credentials: false,
240 exposedHeaders: [],
241 headers: [],
242 maxAge: 86400,
243 methods: [],
244 origins: []
245};
246function getAllOptions(options) {
247 const allOptions = { ...defaultCorsOptions, ...options };
248 const allowedOrigins = parseListValue(allOptions.origins);
249 const allowedHeaders = parseListValue(allOptions.headers);
250 const allowedMethods = parseListValue(allOptions.methods);
251 const exposedHeaders = parseListValue(allOptions.exposedHeaders);
252 const { credentials: allowCredentials, maxAge } = allOptions;
253 return { allowedOrigins, allowedHeaders, allowedMethods, exposedHeaders, allowCredentials, maxAge };
254}
255function parseListValue(value) {
256 return Array.isArray(value) ? value : value.split(",").map((s) => s.trim());
257}
258var cors = (options) => middleware({
259 name: "CORS"
260}, ({ event }) => {
261 const { allowedOrigins, exposedHeaders, allowCredentials } = getAllOptions(options);
262 const origin = event.headers.get("origin");
263 if (allowedOrigins.includes("*")) {
264 event.reply.headers.set("access-control-allow-origin", "*");
265 } else if (origin && (allowedOrigins.includes(origin) || allowedOrigins.some((allowed) => allowed.startsWith("https://*.") && origin.endsWith(allowed.slice(9))))) {
266 event.reply.headers.set("access-control-allow-origin", origin);
267 event.reply.headers.append("vary", "origin");
268 }
269 if (allowCredentials) event.reply.headers.set("access-control-allow-credentials", "true");
270 if (exposedHeaders.length > 0) event.reply.headers.set("access-control-expose-headers", exposedHeaders.join(","));
271 return event.next();
272});
273var preflightHandler = (router, options) => middleware({
274 name: "Preflight handler"
275}, ({ event }) => {
276 if (event.method !== "OPTIONS" /* OPTIONS */) return event.next();
277 const { allowedHeaders, allowedMethods, maxAge } = getAllOptions(options);
278 const requestMethod = event.headers.get("access-control-request-method") ?? void 0;
279 const route = router.find(event.pathname, requestMethod, void 0, true);
280 if (!route) return event.reply.notFound();
281 if (allowedHeaders.length > 0) event.reply.headers.set("access-control-allow-headers", allowedHeaders.join(","));
282 if (allowedMethods.length > 0) event.reply.headers.set("access-control-allow-methods", allowedMethods.join(","));
283 event.reply.headers.set("access-control-max-age", maxAge.toString());
284 event.reply.headers.set("content-length", "0");
285 return event.reply.noContent();
286});
287
288// src/data.ts
289var import_api2 = require("@opentelemetry/api");
290var Data = class _Data {
291 constructor(request, method, pathname, hostname, headers, cookies, query, params, body, _raw, server2) {
292 this.request = request;
293 this.method = method;
294 this.pathname = pathname;
295 this.hostname = hostname;
296 this.headers = headers;
297 this.cookies = cookies;
298 this.query = query;
299 this.params = params;
300 this.body = body;
301 this._raw = _raw;
302 this.server = server2;
303 }
304 static async fromRequest(request, options, serverName) {
305 const url = new URL(request.url);
306 const { raw, body } = await this.parseRequestBody(request);
307 import_api2.trace.getActiveSpan()?.addEvent("convert request to data", {
308 method: request.method,
309 pathname: url.pathname,
310 hostname: url.hostname,
311 protocol: url.protocol,
312 params: url.search
313 });
314 return new _Data(
315 request,
316 request.method.toUpperCase(),
317 url.pathname,
318 url.hostname.toLowerCase(),
319 request.headers,
320 await Cookies.parse(request.headers, options.cookies),
321 Object.fromEntries(url.searchParams.entries()),
322 {},
323 body,
324 raw,
325 { name: serverName }
326 );
327 }
328 static async parseRequestBody(request) {
329 const contentType = request.headers.get("content-type")?.split(";")[0];
330 import_api2.trace.getActiveSpan()?.addEvent("parse request body", {
331 contentType: request.headers.get("content-type") ?? void 0,
332 parsedContentType: contentType
333 });
334 switch (contentType) {
335 case "application/json": {
336 const text = await request.text();
337 return { raw: text, body: JSON.parse(text) };
338 }
339 case "multipart/form-data": {
340 const formData = await request.formData();
341 const body = {};
342 for (const [key, value] of formData.entries()) body[key] = value;
343 return { body };
344 }
345 case "application/x-www-form-urlencoded": {
346 const text = await request.text();
347 const entries = text.split("&").map((x) => x.split("=").map(decodeURIComponent));
348 return { body: Object.fromEntries(entries) };
349 }
350 default:
351 return { body: void 0 };
352 }
353 }
354};
355
356// src/reply.ts
357var import_api3 = require("@opentelemetry/api");
358var Status = /* @__PURE__ */ ((Status2) => {
359 Status2["Continue"] = "continue";
360 Status2["SwitchingProtocols"] = "switchingProtocols";
361 Status2["Processing"] = "processing";
362 Status2["Ok"] = "ok";
363 Status2["Created"] = "created";
364 Status2["Accepted"] = "accepted";
365 Status2["NonAuthoritativeInformation"] = "nonAuthoritativeInformation";
366 Status2["NoContent"] = "noContent";
367 Status2["ResetContent"] = "resetContent";
368 Status2["PartialContent"] = "partialContent";
369 Status2["MultiStatus"] = "multiStatus";
370 Status2["MultipleChoices"] = "multipleChoices";
371 Status2["MovedPermanently"] = "movedPermanently";
372 Status2["MovedTemporarily"] = "movedTemporarily";
373 Status2["SeeOther"] = "seeOther";
374 Status2["NotModified"] = "notModified";
375 Status2["UseProxy"] = "useProxy";
376 Status2["TemporaryRedirect"] = "temporaryRedirect";
377 Status2["PermanentRedirect"] = "permanentRedirect";
378 Status2["BadRequest"] = "badRequest";
379 Status2["Unauthorized"] = "unauthorized";
380 Status2["PaymentRequired"] = "paymentRequired";
381 Status2["Forbidden"] = "forbidden";
382 Status2["NotFound"] = "notFound";
383 Status2["MethodNotAllowed"] = "methodNotAllowed";
384 Status2["NotAcceptable"] = "notAcceptable";
385 Status2["ProxyAuthenticationRequired"] = "proxyAuthenticationRequired";
386 Status2["RequestTimeout"] = "requestTimeout";
387 Status2["Conflict"] = "conflict";
388 Status2["Gone"] = "gone";
389 Status2["LengthRequired"] = "lengthRequired";
390 Status2["PreconditionFailed"] = "preconditionFailed";
391 Status2["RequestTooLong"] = "requestTooLong";
392 Status2["RequestUriTooLong"] = "requestUriTooLong";
393 Status2["UnsupportedMediaType"] = "unsupportedMediaType";
394 Status2["RequestedRangeNotSatisfiable"] = "requestedRangeNotSatisfiable";
395 Status2["ExpectationFailed"] = "expectationFailed";
396 Status2["ImATeapot"] = "imATeapot";
397 Status2["InsufficientSpaceOnResource"] = "insufficientSpaceOnResource";
398 Status2["MethodFailure"] = "methodFailure";
399 Status2["MisdirectedRequest"] = "misdirectedRequest";
400 Status2["UnprocessableEntity"] = "unprocessableEntity";
401 Status2["FailedDependency"] = "failedDependency";
402 Status2["PreconditionRequired"] = "preconditionRequired";
403 Status2["TooManyRequests"] = "tooManyRequests";
404 Status2["RequestHeaderFieldsTooLarge"] = "requestHeaderFieldsTooLarge";
405 Status2["UnavailableForLegalReasons"] = "unavailableForLegalReasons";
406 Status2["InternalServerError"] = "internalServerError";
407 Status2["NotImplemented"] = "notImplemented";
408 Status2["BadGateway"] = "badGateway";
409 Status2["ServiceUnavailable"] = "serviceUnavailable";
410 Status2["GatewayTimeout"] = "gatewayTimeout";
411 Status2["HttpVersionNotSupported"] = "httpVersionNotSupported";
412 Status2["InsufficientStorage"] = "insufficientStorage";
413 Status2["NetworkAuthenticationRequired"] = "networkAuthenticationRequired";
414 return Status2;
415})(Status || {});
416var Reply = class {
417 // Reply data for regular replies
418 body;
419 status = Number.NaN;
420 headers = new Headers();
421 cookies = new Cookies();
422 // Reply data for raw replies
423 _raw;
424 // The date the reply was sent
425 sent;
426 // Convert a reply to a standard response
427 static async toResponse(reply, options) {
428 if (!reply.sent) {
429 recordException("[Comet] No reply was sent for this event.");
430 return new Response(null, { status: 500 });
431 }
432 if (reply._raw !== void 0) {
433 import_api3.trace.getActiveSpan()?.addEvent("return raw response");
434 return reply._raw;
435 }
436 const status = reply.status;
437 const headers = reply.headers;
438 await Cookies.serialize(reply.cookies, reply.headers, options.cookies);
439 if (reply.body instanceof WebSocket) {
440 import_api3.trace.getActiveSpan()?.addEvent("return websocket response");
441 return new Response(null, { status, headers, webSocket: reply.body });
442 }
443 if (reply.body instanceof ReadableStream) {
444 import_api3.trace.getActiveSpan()?.addEvent("return streamed response");
445 return new Response(reply.body, { status, headers });
446 }
447 let body = null;
448 if (reply.body) {
449 headers.set("content-type", "application/json");
450 body = options.dev ? JSON.stringify(reply.body, null, 2) : JSON.stringify(reply.body);
451 }
452 import_api3.trace.getActiveSpan()?.addEvent("convert response");
453 return new Response(body, { status, headers });
454 }
455 // Send a regular reply
456 send(status, body) {
457 if (this.sent) {
458 recordException("[Comet] Cannot send a reply after one has already been sent.");
459 return this;
460 }
461 this.status = status;
462 this.body = body;
463 this.sent = /* @__PURE__ */ new Date();
464 import_api3.trace.getActiveSpan()?.addEvent("send reply", {
465 status,
466 sent: +this.sent
467 });
468 return this;
469 }
470 // Send a raw reply
471 raw(response) {
472 if (this.sent) {
473 recordException("[Comet] Cannot send a reply after one has already been sent.");
474 return this;
475 }
476 this._raw = response;
477 this.sent = /* @__PURE__ */ new Date();
478 import_api3.trace.getActiveSpan()?.addEvent("raw reply", {
479 sent: +this.sent
480 });
481 return this;
482 }
483 // Send a custom HTTP response
484 custom(status, body) {
485 return this.send(status, body);
486 }
487 // Send an HTTP `100 Continue` informational response
488 continue(body) {
489 return this.send(100, body);
490 }
491 // Send an HTTP `101 Switching Protocols` informational response
492 switchingProtocols(body) {
493 return this.send(101, body);
494 }
495 // Send an HTTP `102 Processing` informational response
496 processing(body) {
497 return this.send(102, body);
498 }
499 // Send an HTTP `200 OK` successful response
500 ok(body) {
501 return this.send(200, body);
502 }
503 // Send an HTTP `201 Created` successful response
504 created(body) {
505 return this.send(201, body);
506 }
507 // Send an HTTP `202 Accepted` successful response
508 accepted(body) {
509 return this.send(202, body);
510 }
511 // Send an HTTP `203 Non-Authoritative Information` successful response
512 nonAuthoritativeInformation(body) {
513 return this.send(203, body);
514 }
515 // Send an HTTP `204 No Content` successful response
516 noContent(body) {
517 return this.send(204, body);
518 }
519 // Send an HTTP `205 Reset Content` successful response
520 resetContent(body) {
521 return this.send(205, body);
522 }
523 // Send an HTTP `206 Partial Content` successful response
524 partialContent(body) {
525 return this.send(206, body);
526 }
527 // Send an HTTP `207 Multi-Status` successful response
528 multiStatus(body) {
529 return this.send(207, body);
530 }
531 // Send an HTTP `300 Multiple Choices` redirection response
532 multipleChoices(body) {
533 return this.send(300, body);
534 }
535 // Send an HTTP `301 Moved Permanently` redirection response
536 movedPermanently(body) {
537 return this.send(301, body);
538 }
539 // Send an HTTP `302 Moved Temporarily` redirection response
540 movedTemporarily(body) {
541 return this.send(302, body);
542 }
543 // Send an HTTP `303 See Other` redirection response
544 seeOther(body) {
545 return this.send(303, body);
546 }
547 // Send an HTTP `304 Not Modified` redirection response
548 notModified(body) {
549 return this.send(304, body);
550 }
551 // Send an HTTP `305 Use Proxy` redirection response
552 useProxy(body) {
553 return this.send(305, body);
554 }
555 // Send an HTTP `307 Temporary Redirect` redirection response
556 temporaryRedirect(body) {
557 return this.send(307, body);
558 }
559 // Send an HTTP `308 Permanent Redirect` redirection response
560 permanentRedirect(body) {
561 return this.send(308, body);
562 }
563 // Send an HTTP `400 Bad Request` client error response
564 badRequest(body) {
565 return this.send(400, body);
566 }
567 // Send an HTTP `401 Unauthorized` client error response
568 unauthorized(body) {
569 return this.send(401, body);
570 }
571 // Send an HTTP `402 Payment Required` client error response
572 paymentRequired(body) {
573 return this.send(402, body);
574 }
575 // Send an HTTP `403 Forbidden` client error response
576 forbidden(body) {
577 return this.send(403, body);
578 }
579 // Send an HTTP `404 Not Found` client error response
580 notFound(body) {
581 return this.send(404, body);
582 }
583 // Send an HTTP `405 Method Not Allowed` client error response
584 methodNotAllowed(body) {
585 return this.send(405, body);
586 }
587 // Send an HTTP `406 Not Acceptable` client error response
588 notAcceptable(body) {
589 return this.send(406, body);
590 }
591 // Send an HTTP `407 Proxy Authentication Required` client error response
592 proxyAuthenticationRequired(body) {
593 return this.send(407, body);
594 }
595 // Send an HTTP `408 Request Timeout` client error response
596 requestTimeout(body) {
597 return this.send(408, body);
598 }
599 // Send an HTTP `409 Conflict` client error response
600 conflict(body) {
601 return this.send(409, body);
602 }
603 // Send an HTTP `410 Gone` client error response
604 gone(body) {
605 return this.send(410, body);
606 }
607 // Send an HTTP `411 Length Required` client error response
608 lengthRequired(body) {
609 return this.send(411, body);
610 }
611 // Send an HTTP `412 Precondition Failed` client error response
612 preconditionFailed(body) {
613 return this.send(412, body);
614 }
615 // Send an HTTP `413 Request Too Long` client error response
616 requestTooLong(body) {
617 return this.send(413, body);
618 }
619 // Send an HTTP `414 Request URI Too Long` client error response
620 requestUriTooLong(body) {
621 return this.send(414, body);
622 }
623 // Send an HTTP `415 Unsupported Media Type` client error response
624 unsupportedMediaType(body) {
625 return this.send(415, body);
626 }
627 // Send an HTTP `416 Requested Range Not Satisfiable` client error response
628 requestedRangeNotSatisfiable(body) {
629 return this.send(416, body);
630 }
631 // Send an HTTP `417 Expectation Failed` client error response
632 expectationFailed(body) {
633 return this.send(417, body);
634 }
635 // Send an HTTP `418 I'm a teapot` client error response
636 imATeapot(body) {
637 return this.send(418, body);
638 }
639 // Send an HTTP `419 Insufficient Space On Resource` client error response
640 insufficientSpaceOnResource(body) {
641 return this.send(419, body);
642 }
643 // Send an HTTP `420 Method Failure` client error response
644 methodFailure(body) {
645 return this.send(420, body);
646 }
647 // Send an HTTP `421 Misdirected Request` client error response
648 misdirectedRequest(body) {
649 return this.send(421, body);
650 }
651 // Send an HTTP `422 Unprocessable Entity` client error response
652 unprocessableEntity(body) {
653 return this.send(422, body);
654 }
655 // Send an HTTP `424 Failed Dependency` client error response
656 failedDependency(body) {
657 return this.send(424, body);
658 }
659 // Send an HTTP `428 Precondition Required` client error response
660 preconditionRequired(body) {
661 return this.send(428, body);
662 }
663 // Send an HTTP `429 Too Many Requests` client error response
664 tooManyRequests(body) {
665 return this.send(429, body);
666 }
667 // Send an HTTP `431 Request Header Fields Too Large` client error response
668 requestHeaderFieldsTooLarge(body) {
669 return this.send(431, body);
670 }
671 // Send an HTTP `451 Unavailable For Legal Reasons` client error response
672 unavailableForLegalReasons(body) {
673 return this.send(451, body);
674 }
675 // Send an HTTP `500 Internal Server Error` server error response
676 internalServerError(body) {
677 return this.send(500, body);
678 }
679 // Send an HTTP `501 Not Implemented` server error response
680 notImplemented(body) {
681 return this.send(501, body);
682 }
683 // Send an HTTP `502 Bad Gateway` server error response
684 badGateway(body) {
685 return this.send(502, body);
686 }
687 // Send an HTTP `503 Service Unavailable` server error response
688 serviceUnavailable(body) {
689 return this.send(503, body);
690 }
691 // Send an HTTP `504 Gateway Timeout` server error response
692 gatewayTimeout(body) {
693 return this.send(504, body);
694 }
695 // Send an HTTP `505 Http Version Not Supported` server error response
696 httpVersionNotSupported(body) {
697 return this.send(505, body);
698 }
699 // Send an HTTP `507 Insufficient Storage` server error response
700 insufficientStorage(body) {
701 return this.send(507, body);
702 }
703 // Send an HTTP `511 Network Authentication Required` server error response
704 networkAuthenticationRequired(body) {
705 return this.send(511, body);
706 }
707};
708
709// src/utils.ts
710var BASE_URL = "https://comet";
711function isValidPathname(value) {
712 if (typeof value !== "string") return false;
713 try {
714 new URLPattern(value, BASE_URL);
715 return true;
716 } catch {
717 return false;
718 }
719}
720function isValidCompatibilityDate(value) {
721 return typeof value === "string" && !Number.isNaN(new Date(value).valueOf());
722}
723function comparePathnames(check, against) {
724 if (!against || against === "*") return true;
725 return new URLPattern(against, BASE_URL).test(check, BASE_URL);
726}
727function compareMethods(check, against) {
728 if (!against || against === "ALL" /* ALL */) return true;
729 return check === against;
730}
731function compareCompatibilityDates(check, against) {
732 if (!against) return true;
733 if (!check) return false;
734 return new Date(check) >= new Date(against);
735}
736function getPathnameParameters(pathname, template) {
737 const result = new URLPattern(template, BASE_URL).exec(pathname, BASE_URL);
738 return result?.pathname?.groups ?? {};
739}
740
741// src/router.ts
742var Router = class {
743 // Take router options
744 constructor(options) {
745 this.options = options;
746 }
747 // Registry of routes
748 routes = [];
749 ready = true;
750 // Register a new route
751 register = (options, handler) => {
752 const _register = (inputMethod) => {
753 const pathname = `${this.options.prefix ?? ""}${options.pathname ?? "*"}`;
754 const method2 = inputMethod ?? "ALL" /* ALL */;
755 const compatibilityDate = options.compatibilityDate;
756 const name2 = options.name ?? `${method2} ${pathname}${compatibilityDate ? ` (${compatibilityDate})` : ""}`;
757 if (!isValidPathname(pathname)) {
758 recordException(`[Comet] Failed to set up route '${name2}' due to an invalid pathname.`);
759 return;
760 }
761 if (options.compatibilityDate !== void 0 && !isValidCompatibilityDate(options.compatibilityDate)) {
762 recordException(`[Comet] Failed to set up route '${name2}' due to an invalid compatibility date.`);
763 return;
764 }
765 const schemas = { body: options.body, params: options.params, query: options.query };
766 this.routes.push({ ...options, pathname, method: method2, name: name2, handler, schemas });
767 };
768 const { method } = options;
769 if (Array.isArray(method)) {
770 for (const each of method) _register(each);
771 } else {
772 _register(method);
773 }
774 this.ready = false;
775 };
776 // Find a route on a server by pathname, method and compatibility date
777 find = (pathname, method, compatibilityDate, ignoreCompatibilityDate) => {
778 for (const route of this.routes) {
779 const doPathnamesMatch = comparePathnames(pathname, route.pathname);
780 if (!doPathnamesMatch) continue;
781 const doMethodsMatch = compareMethods(method, route.method);
782 if (!doMethodsMatch) continue;
783 if (ignoreCompatibilityDate) return route;
784 const doCompatibilityDatesMatch = compareCompatibilityDates(compatibilityDate, route.compatibilityDate);
785 if (doCompatibilityDatesMatch) return route;
786 }
787 };
788 // Initialize router by sorting the routes by compatibility date in descending order to ensure the correct functioning of the find algorithm
789 init = () => {
790 if (this.ready) return;
791 this.routes.sort((a, b) => {
792 if (a.pathname !== b.pathname || a.method !== b.method) return 0;
793 return compareCompatibilityDates(a.compatibilityDate, b.compatibilityDate) ? -1 : 1;
794 });
795 this.ready = true;
796 };
797 getRoutes = () => this.routes;
798};
799
800// src/schemaValidation.ts
801var import_api4 = require("@opentelemetry/api");
802var schemaValidation = (route) => middleware({
803 name: "Schema validation"
804}, async ({ event }) => {
805 const { body: bodySchema, params: paramsSchema, query: querySchema } = route.schemas;
806 const paramsResult = paramsSchema?.safeParse(event.params);
807 import_api4.trace.getActiveSpan()?.addEvent("params schema parse", {
808 success: paramsResult?.success,
809 errors: paramsResult?.success ? void 0 : paramsResult?.error.issues.map((issue) => issue.message)
810 });
811 const queryResult = querySchema?.safeParse(event.query);
812 import_api4.trace.getActiveSpan()?.addEvent("query schema parse", {
813 success: queryResult?.success,
814 errors: paramsResult?.success ? void 0 : paramsResult?.error.issues.map((issue) => issue.message)
815 });
816 const bodyResult = bodySchema?.safeParse(event.body);
817 import_api4.trace.getActiveSpan()?.addEvent("body schema parse", {
818 success: bodyResult?.success,
819 errors: paramsResult?.success ? void 0 : paramsResult?.error.issues.map((issue) => issue.message)
820 });
821 const errors = {};
822 if (paramsResult?.success === false) errors.params = paramsResult.error.issues;
823 if (queryResult?.success === false) errors.query = queryResult.error.issues;
824 if (bodyResult?.success === false) errors.body = bodyResult.error.issues;
825 if (errors.body || errors.params || errors.query) {
826 return event.reply.badRequest({ success: false, errors });
827 }
828 if (paramsResult?.success) event.params = paramsResult.data;
829 if (queryResult?.success) event.query = queryResult.data;
830 if (bodyResult?.success) event.body = bodyResult.data;
831 return event.next();
832});
833
834// src/server.ts
835var import_api5 = require("@opentelemetry/api");
836var Server = class {
837 constructor(options = {}) {
838 this.options = options;
839 this.router = new Router(options);
840 this.route = this.router.register;
841 }
842 router;
843 route;
844 handler = async (request, env, ctxOrState) => {
845 return import_api5.trace.getTracer(name, version).startActiveSpan("Comet Handler", {
846 kind: import_api5.SpanKind.SERVER,
847 attributes: {
848 name: this.options.name
849 }
850 }, async (span) => {
851 try {
852 this.router.init();
853 const data = await Data.fromRequest(request, this.options, this.options.name);
854 const reply = new Reply();
855 const isDurableObject = "id" in ctxOrState;
856 const event = {
857 ...data,
858 reply,
859 next,
860 isDurableObject,
861 ...isDurableObject ? { state: ctxOrState } : { ctx: ctxOrState }
862 };
863 const input = { event, env, logger };
864 span.setAttribute("comet.server.durable_object", isDurableObject);
865 if (this.options.before) {
866 for (const mw of this.options.before) {
867 await import_api5.trace.getTracer(name, version).startActiveSpan(
868 `Comet Middleware${mw.name ? ` ${mw.name}` : ""}`,
869 {
870 attributes: {
871 "comet.mw.name": mw.name,
872 "comet.mw.type": "global-before"
873 }
874 },
875 async (span2) => {
876 await mw.handler(input);
877 span2.end();
878 }
879 );
880 if (event.reply.sent) break;
881 }
882 }
883 if (!event.reply.sent) {
884 await import_api5.trace.getTracer(name, version).startActiveSpan(
885 "Comet CORS Middleware",
886 {
887 attributes: {
888 "comet.mw.name": "CORS",
889 "comet.mw.type": "global-before",
890 "comet.mw.cors.origin": event.headers.get("origin") ?? void 0,
891 "comet.mw.cors.method": event.method
892 }
893 },
894 async (span2) => {
895 await cors(this.options.cors).handler(input);
896 span2.end();
897 }
898 );
899 }
900 if (!event.reply.sent) {
901 await import_api5.trace.getTracer(name, version).startActiveSpan(
902 "Comet Routing",
903 {
904 attributes: {
905 "comet.routing.compatibility_date": event.headers.get("x-compatibility-date") ?? void 0,
906 "comet.routing.pathname": event.pathname,
907 "comet.routing.method": event.method
908 }
909 },
910 async (span2) => {
911 const compatibilityDate = event.headers.get("x-compatibility-date") ?? void 0;
912 if (compatibilityDate && new Date(compatibilityDate) > /* @__PURE__ */ new Date() && !this.options.dev) {
913 event.reply.badRequest({ message: "Invalid compatibility date" });
914 } else {
915 const route = this.router.find(event.pathname, event.method, compatibilityDate);
916 if (!route) {
917 if (event.method === "OPTIONS" /* OPTIONS */) {
918 await preflightHandler(this.router, this.options.cors).handler(input);
919 } else {
920 event.reply.notFound();
921 }
922 } else {
923 event.params = getPathnameParameters(event.pathname, route.pathname);
924 if (!event.reply.sent) schemaValidation(route).handler(input);
925 if (route.before) {
926 for (const mw of route.before) {
927 await import_api5.trace.getTracer(name, version).startActiveSpan(
928 `Comet Middleware${mw.name ? ` ${mw.name}` : ""}`,
929 {
930 attributes: {
931 "comet.mw.name": mw.name,
932 "comet.mw.type": "local-before"
933 }
934 },
935 async (span3) => {
936 await mw.handler(input);
937 span3.end();
938 }
939 );
940 if (event.reply.sent) break;
941 }
942 }
943 if (!event.reply.sent) {
944 await import_api5.trace.getTracer(name, version).startActiveSpan(
945 "Comet Main Handler",
946 {
947 attributes: {
948 "comet.route.name": route.name,
949 "comet.route.pathname": route.pathname,
950 "comet.route.compatibility_date": route.compatibilityDate,
951 "comet.route.has_body_schema": !!route.schemas.body,
952 "comet.route.has_query_schema": !!route.schemas.query,
953 "comet.route.has_params_schema": !!route.schemas.params,
954 "comet.route.method": route.method
955 }
956 },
957 async (span3) => {
958 await route.handler(input);
959 span3.end();
960 }
961 );
962 }
963 if (route.after) {
964 if (isDurableObject) {
965 for (const mw of route.after) {
966 await import_api5.trace.getTracer(name, version).startActiveSpan(
967 `Comet Middleware${mw.name ? ` ${mw.name}` : ""}`,
968 {
969 attributes: {
970 "comet.mw.name": mw.name,
971 "comet.mw.type": "local-after"
972 }
973 },
974 async (span3) => {
975 await mw.handler(input);
976 span3.end();
977 }
978 );
979 }
980 } else {
981 ctxOrState.waitUntil(Promise.allSettled(route.after.map(async (mw) => {
982 const span3 = import_api5.trace.getTracer(name, version).startSpan(`Comet Middleware${mw.name ? ` ${mw.name}` : ""}`, {
983 attributes: {
984 "comet.mw.name": mw.name,
985 "comet.mw.type": "local-after"
986 }
987 });
988 await mw.handler(input);
989 span3.end();
990 })));
991 }
992 }
993 }
994 }
995 span2.end();
996 }
997 );
998 }
999 if (this.options.after) {
1000 if (isDurableObject) {
1001 for (const mw of this.options.after) {
1002 await import_api5.trace.getTracer(name, version).startActiveSpan(
1003 `Comet Middleware${mw.name ? ` ${mw.name}` : ""}`,
1004 {
1005 attributes: {
1006 "comet.mw.name": mw.name,
1007 "comet.mw.type": "global-after"
1008 }
1009 },
1010 async (span2) => {
1011 await mw.handler(input);
1012 span2.end();
1013 }
1014 );
1015 }
1016 } else {
1017 ctxOrState.waitUntil(Promise.allSettled(this.options.after.map(async (mw) => {
1018 const span2 = import_api5.trace.getTracer(name, version).startSpan(`Comet Middleware${mw.name ? ` ${mw.name}` : ""}`, {
1019 attributes: {
1020 "comet.mw.name": mw.name,
1021 "comet.mw.type": "global-after"
1022 }
1023 });
1024 await mw.handler(input);
1025 span2.end();
1026 })));
1027 }
1028 }
1029 span.end();
1030 return await Reply.toResponse(event.reply, this.options);
1031 } catch (error) {
1032 recordException("[Comet] Failed to handle request.");
1033 recordException(error);
1034 span.end();
1035 return new Response(null, { status: 500 });
1036 }
1037 });
1038 };
1039 static getRouter(server2) {
1040 return server2.router;
1041 }
1042};
1043function server(options) {
1044 return new Server(options);
1045}