routup 6.0.0-beta.4 → 6.0.0

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.
@@ -490,6 +490,152 @@ function setResponseHeaderContentType(event, input, ifNotExists) {
490
490
  if (contentType) event.response.headers.set(HeaderName.CONTENT_TYPE, contentType);
491
491
  }
492
492
  //#endregion
493
+ //#region src/response/helpers/send-file.ts
494
+ async function sendFile(event, options) {
495
+ let stats;
496
+ if (typeof options.stats === "function") stats = await options.stats();
497
+ else stats = options.stats;
498
+ const name = options.name || stats.name;
499
+ const { headers } = event.response;
500
+ const disposition = options.disposition ?? (options.attachment ? "attachment" : void 0);
501
+ if (name) {
502
+ const fileName = basename(name);
503
+ if (disposition) {
504
+ if (!headers.get(HeaderName.CONTENT_DISPOSITION)) if (disposition === "inline") setResponseHeaderInline(event, fileName);
505
+ else setResponseHeaderAttachment(event, fileName);
506
+ } else setResponseContentTypeByFileName(event, fileName);
507
+ }
508
+ const contentOptions = {};
509
+ let statusCode = event.response.status;
510
+ if (stats.size) {
511
+ const rangeHeader = event.headers.get(HeaderName.RANGE);
512
+ if (rangeHeader) {
513
+ const [x, y] = rangeHeader.replace("bytes=", "").split("-");
514
+ const parsedStart = Number.parseInt(x, 10);
515
+ const parsedEnd = Number.parseInt(y, 10);
516
+ contentOptions.start = Number.isFinite(parsedStart) && parsedStart >= 0 ? parsedStart : 0;
517
+ contentOptions.end = Number.isFinite(parsedEnd) && parsedEnd >= 0 ? Math.min(parsedEnd, stats.size - 1) : stats.size - 1;
518
+ if (contentOptions.start >= stats.size || contentOptions.start > contentOptions.end) {
519
+ const rangeHeaders = new Headers(headers);
520
+ rangeHeaders.set(HeaderName.CONTENT_RANGE, `bytes */${stats.size}`);
521
+ return new Response(null, {
522
+ status: 416,
523
+ headers: rangeHeaders
524
+ });
525
+ }
526
+ headers.set(HeaderName.CONTENT_RANGE, `bytes ${contentOptions.start}-${contentOptions.end}/${stats.size}`);
527
+ headers.set(HeaderName.CONTENT_LENGTH, `${contentOptions.end - contentOptions.start + 1}`);
528
+ statusCode = 206;
529
+ } else headers.set(HeaderName.CONTENT_LENGTH, `${stats.size}`);
530
+ headers.set(HeaderName.ACCEPT_RANGES, "bytes");
531
+ if (stats.mtime) {
532
+ const mtime = new Date(stats.mtime);
533
+ headers.set(HeaderName.LAST_MODIFIED, mtime.toUTCString());
534
+ headers.set(HeaderName.ETag, `W/"${stats.size}-${mtime.getTime()}"`);
535
+ }
536
+ }
537
+ const content = await options.content(contentOptions);
538
+ return new Response(content, {
539
+ status: statusCode,
540
+ headers
541
+ });
542
+ }
543
+ //#endregion
544
+ //#region src/request/helpers/header.ts
545
+ function getRequestHeader(event, name) {
546
+ return event.headers.get(name);
547
+ }
548
+ //#endregion
549
+ //#region src/request/helpers/negotiator.ts
550
+ const NEGOTIATOR_KEY = Symbol.for("routup:negotiator");
551
+ function headersToPlainObject(headers) {
552
+ const result = {};
553
+ headers.forEach((value, key) => {
554
+ result[key] = value;
555
+ });
556
+ return result;
557
+ }
558
+ function useRequestNegotiator(event) {
559
+ let value = event.store[NEGOTIATOR_KEY];
560
+ if (value) return value;
561
+ value = new Negotiator({ headers: headersToPlainObject(event.headers) });
562
+ event.store[NEGOTIATOR_KEY] = value;
563
+ return value;
564
+ }
565
+ //#endregion
566
+ //#region src/request/helpers/header-accept.ts
567
+ function getRequestAcceptableContentTypes(event) {
568
+ return useRequestNegotiator(event).mediaTypes();
569
+ }
570
+ function getRequestAcceptableContentType(event, input) {
571
+ input = input || [];
572
+ const items = Array.isArray(input) ? input : [input];
573
+ if (items.length === 0) return getRequestAcceptableContentTypes(event).shift();
574
+ if (!getRequestHeader(event, HeaderName.ACCEPT)) return items[0];
575
+ let polluted = false;
576
+ const mimeTypes = [];
577
+ for (const item of items) {
578
+ const mimeType = getMimeType(item);
579
+ if (mimeType) mimeTypes.push(mimeType);
580
+ else polluted = true;
581
+ }
582
+ const matches = useRequestNegotiator(event).mediaTypes(mimeTypes);
583
+ if (matches.length > 0) {
584
+ if (polluted) return items[0];
585
+ return items[mimeTypes.indexOf(matches[0])];
586
+ }
587
+ }
588
+ //#endregion
589
+ //#region src/response/helpers/send-format.ts
590
+ function sendFormat(event, input) {
591
+ const { default: formatDefault, ...formats } = input;
592
+ const contentTypes = Object.keys(formats);
593
+ if (contentTypes.length === 0) return formatDefault();
594
+ const contentType = getRequestAcceptableContentType(event, contentTypes);
595
+ if (contentType && formats[contentType]) return formats[contentType]();
596
+ return formatDefault();
597
+ }
598
+ //#endregion
599
+ //#region src/response/helpers/send-redirect.ts
600
+ function escapeHtml(str) {
601
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
602
+ }
603
+ function isAllowedRedirectUrl(location) {
604
+ if (location.startsWith("//")) return false;
605
+ if (location.startsWith("/") || location.startsWith(".")) return true;
606
+ try {
607
+ const url = new URL(location);
608
+ return url.protocol === "http:" || url.protocol === "https:";
609
+ } catch {
610
+ return true;
611
+ }
612
+ }
613
+ function sendRedirect(event, location, statusCode = 302) {
614
+ if (!isAllowedRedirectUrl(location)) throw new AppError({
615
+ status: 400,
616
+ message: "Invalid redirect URL scheme."
617
+ });
618
+ const sanitizedLocation = sanitizeHeaderValue(location);
619
+ const html = `<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=${escapeHtml(location)}"></head></html>`;
620
+ const headers = new Headers(event.response.headers);
621
+ headers.set("location", sanitizedLocation);
622
+ headers.set("content-type", "text/html; charset=utf-8");
623
+ headers.delete("content-length");
624
+ return new Response(html, {
625
+ status: statusCode,
626
+ headers
627
+ });
628
+ }
629
+ //#endregion
630
+ //#region src/response/helpers/send-stream.ts
631
+ function sendStream(event, stream) {
632
+ const { status, headers } = event.response;
633
+ return new Response(stream, {
634
+ status,
635
+ headers
636
+ });
637
+ }
638
+ //#endregion
493
639
  //#region src/error/is.ts
494
640
  function isError(input) {
495
641
  return hasInstanceof(input, ErrorSymbol);
@@ -638,164 +784,6 @@ function toResponse(value, event) {
638
784
  });
639
785
  }
640
786
  //#endregion
641
- //#region src/response/helpers/send-accepted.ts
642
- async function sendAccepted(event, data) {
643
- event.response.status = 202;
644
- return await toResponse(data ?? "", event);
645
- }
646
- //#endregion
647
- //#region src/response/helpers/send-created.ts
648
- async function sendCreated(event, data) {
649
- event.response.status = 201;
650
- return await toResponse(data ?? "", event);
651
- }
652
- //#endregion
653
- //#region src/response/helpers/send-file.ts
654
- async function sendFile(event, options) {
655
- let stats;
656
- if (typeof options.stats === "function") stats = await options.stats();
657
- else stats = options.stats;
658
- const name = options.name || stats.name;
659
- const { headers } = event.response;
660
- const disposition = options.disposition ?? (options.attachment ? "attachment" : void 0);
661
- if (name) {
662
- const fileName = basename(name);
663
- if (disposition) {
664
- if (!headers.get(HeaderName.CONTENT_DISPOSITION)) if (disposition === "inline") setResponseHeaderInline(event, fileName);
665
- else setResponseHeaderAttachment(event, fileName);
666
- } else setResponseContentTypeByFileName(event, fileName);
667
- }
668
- const contentOptions = {};
669
- let statusCode = event.response.status;
670
- if (stats.size) {
671
- const rangeHeader = event.headers.get(HeaderName.RANGE);
672
- if (rangeHeader) {
673
- const [x, y] = rangeHeader.replace("bytes=", "").split("-");
674
- const parsedStart = Number.parseInt(x, 10);
675
- const parsedEnd = Number.parseInt(y, 10);
676
- contentOptions.start = Number.isFinite(parsedStart) && parsedStart >= 0 ? parsedStart : 0;
677
- contentOptions.end = Number.isFinite(parsedEnd) && parsedEnd >= 0 ? Math.min(parsedEnd, stats.size - 1) : stats.size - 1;
678
- if (contentOptions.start >= stats.size || contentOptions.start > contentOptions.end) {
679
- const rangeHeaders = new Headers(headers);
680
- rangeHeaders.set(HeaderName.CONTENT_RANGE, `bytes */${stats.size}`);
681
- return new Response(null, {
682
- status: 416,
683
- headers: rangeHeaders
684
- });
685
- }
686
- headers.set(HeaderName.CONTENT_RANGE, `bytes ${contentOptions.start}-${contentOptions.end}/${stats.size}`);
687
- headers.set(HeaderName.CONTENT_LENGTH, `${contentOptions.end - contentOptions.start + 1}`);
688
- statusCode = 206;
689
- } else headers.set(HeaderName.CONTENT_LENGTH, `${stats.size}`);
690
- headers.set(HeaderName.ACCEPT_RANGES, "bytes");
691
- if (stats.mtime) {
692
- const mtime = new Date(stats.mtime);
693
- headers.set(HeaderName.LAST_MODIFIED, mtime.toUTCString());
694
- headers.set(HeaderName.ETag, `W/"${stats.size}-${mtime.getTime()}"`);
695
- }
696
- }
697
- const content = await options.content(contentOptions);
698
- return new Response(content, {
699
- status: statusCode,
700
- headers
701
- });
702
- }
703
- //#endregion
704
- //#region src/request/helpers/header.ts
705
- function getRequestHeader(event, name) {
706
- return event.headers.get(name);
707
- }
708
- //#endregion
709
- //#region src/request/helpers/negotiator.ts
710
- const NEGOTIATOR_KEY = Symbol.for("routup:negotiator");
711
- function headersToPlainObject(headers) {
712
- const result = {};
713
- headers.forEach((value, key) => {
714
- result[key] = value;
715
- });
716
- return result;
717
- }
718
- function useRequestNegotiator(event) {
719
- let value = event.store[NEGOTIATOR_KEY];
720
- if (value) return value;
721
- value = new Negotiator({ headers: headersToPlainObject(event.headers) });
722
- event.store[NEGOTIATOR_KEY] = value;
723
- return value;
724
- }
725
- //#endregion
726
- //#region src/request/helpers/header-accept.ts
727
- function getRequestAcceptableContentTypes(event) {
728
- return useRequestNegotiator(event).mediaTypes();
729
- }
730
- function getRequestAcceptableContentType(event, input) {
731
- input = input || [];
732
- const items = Array.isArray(input) ? input : [input];
733
- if (items.length === 0) return getRequestAcceptableContentTypes(event).shift();
734
- if (!getRequestHeader(event, HeaderName.ACCEPT)) return items[0];
735
- let polluted = false;
736
- const mimeTypes = [];
737
- for (const item of items) {
738
- const mimeType = getMimeType(item);
739
- if (mimeType) mimeTypes.push(mimeType);
740
- else polluted = true;
741
- }
742
- const matches = useRequestNegotiator(event).mediaTypes(mimeTypes);
743
- if (matches.length > 0) {
744
- if (polluted) return items[0];
745
- return items[mimeTypes.indexOf(matches[0])];
746
- }
747
- }
748
- //#endregion
749
- //#region src/response/helpers/send-format.ts
750
- function sendFormat(event, input) {
751
- const { default: formatDefault, ...formats } = input;
752
- const contentTypes = Object.keys(formats);
753
- if (contentTypes.length === 0) return formatDefault();
754
- const contentType = getRequestAcceptableContentType(event, contentTypes);
755
- if (contentType && formats[contentType]) return formats[contentType]();
756
- return formatDefault();
757
- }
758
- //#endregion
759
- //#region src/response/helpers/send-redirect.ts
760
- function escapeHtml(str) {
761
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
762
- }
763
- function isAllowedRedirectUrl(location) {
764
- if (location.startsWith("//")) return false;
765
- if (location.startsWith("/") || location.startsWith(".")) return true;
766
- try {
767
- const url = new URL(location);
768
- return url.protocol === "http:" || url.protocol === "https:";
769
- } catch {
770
- return true;
771
- }
772
- }
773
- function sendRedirect(event, location, statusCode = 302) {
774
- if (!isAllowedRedirectUrl(location)) throw new AppError({
775
- status: 400,
776
- message: "Invalid redirect URL scheme."
777
- });
778
- const sanitizedLocation = sanitizeHeaderValue(location);
779
- const html = `<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=${escapeHtml(location)}"></head></html>`;
780
- const headers = new Headers(event.response.headers);
781
- headers.set("location", sanitizedLocation);
782
- headers.set("content-type", "text/html; charset=utf-8");
783
- headers.delete("content-length");
784
- return new Response(html, {
785
- status: statusCode,
786
- headers
787
- });
788
- }
789
- //#endregion
790
- //#region src/response/helpers/send-stream.ts
791
- function sendStream(event, stream) {
792
- const { status, headers } = event.response;
793
- return new Response(stream, {
794
- status,
795
- headers
796
- });
797
- }
798
- //#endregion
799
787
  //#region src/dispatcher/module.ts
800
788
  var DispatcherEvent = class {
801
789
  request;
@@ -2758,6 +2746,6 @@ var App = class App {
2758
2746
  }
2759
2747
  };
2760
2748
  //#endregion
2761
- export { setResponseHeaderContentType as $, isWebHandler as A, sendStream as B, getRequestAcceptableCharset as C, isHandler as D, matchHandlerMethod as E, defineCoreHandler as F, useRequestNegotiator as G, sendFormat as H, Handler as I, sendCreated as J, getRequestHeader as K, HandlerSymbol as L, fromNodeHandler as M, fromNodeMiddleware as N, isHandlerOptions as O, defineErrorHandler as P, isError as Q, HandlerType as R, getRequestAcceptableEncodings as S, isRequestCacheable as T, getRequestAcceptableContentType as U, sendRedirect as V, getRequestAcceptableContentTypes as W, toResponse as X, sendAccepted as Y, createError as Z, getRequestHostName as _, LinearRouter as a, createEventStream as at, getRequestAcceptableLanguages as b, PluginNotInstalledError as c, ErrorSymbol as ct, isPluginError as d, HeaderName as dt, setResponseHeaderAttachment as et, PluginErrorCode as f, MethodName as ft, getRequestIP as g, getRequestProtocol as h, TrieRouter as i, appendResponseHeaderDirective as it, isWebHandlerProvider as j, fromWebHandler as k, PluginInstallError as l, setResponseCacheHeaders as lt, PathMatcher as m, normalizeAppOptions as n, setResponseContentTypeByFileName as nt, buildRoutePathMatcher as o, serializeEventStreamMessage as ot, isPath as p, LruCache as pt, sendFile as q, SmartRouter as r, appendResponseHeader as rt, isPlugin as s, AppError as st, App as t, setResponseHeaderInline as tt, PluginError as u, AppEvent as ut, matchRequestContentType as v, getRequestAcceptableCharsets as w, getRequestAcceptableEncoding as x, getRequestAcceptableLanguage as y, DispatcherEvent as z };
2749
+ export { setResponseHeaderInline as $, isWebHandler as A, toResponse as B, getRequestAcceptableCharset as C, isHandler as D, matchHandlerMethod as E, defineCoreHandler as F, sendFormat as G, isError as H, Handler as I, useRequestNegotiator as J, getRequestAcceptableContentType as K, HandlerSymbol as L, fromNodeHandler as M, fromNodeMiddleware as N, isHandlerOptions as O, defineErrorHandler as P, setResponseHeaderAttachment as Q, HandlerType as R, getRequestAcceptableEncodings as S, isRequestCacheable as T, sendStream as U, createError as V, sendRedirect as W, sendFile as X, getRequestHeader as Y, setResponseHeaderContentType as Z, getRequestHostName as _, LinearRouter as a, AppError as at, getRequestAcceptableLanguages as b, PluginNotInstalledError as c, AppEvent as ct, isPluginError as d, LruCache as dt, setResponseContentTypeByFileName as et, PluginErrorCode as f, getRequestIP as g, getRequestProtocol as h, TrieRouter as i, serializeEventStreamMessage as it, isWebHandlerProvider as j, fromWebHandler as k, PluginInstallError as l, HeaderName as lt, PathMatcher as m, normalizeAppOptions as n, appendResponseHeaderDirective as nt, buildRoutePathMatcher as o, ErrorSymbol as ot, isPath as p, getRequestAcceptableContentTypes as q, SmartRouter as r, createEventStream as rt, isPlugin as s, setResponseCacheHeaders as st, App as t, appendResponseHeader as tt, PluginError as u, MethodName as ut, matchRequestContentType as v, getRequestAcceptableCharsets as w, getRequestAcceptableEncoding as x, getRequestAcceptableLanguage as y, DispatcherEvent as z };
2762
2750
 
2763
- //# sourceMappingURL=src-DE8cCC5t.mjs.map
2751
+ //# sourceMappingURL=src-CA6xFXqy.mjs.map