tw-framework 1.0.5 → 1.0.6
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/testing.cjs +1640 -1063
- package/dist/tw.mjs +7823 -840
- package/package.json +1 -1
package/dist/testing.cjs
CHANGED
|
@@ -39,10 +39,20 @@ function getActivePipeline() {
|
|
|
39
39
|
function revalidatePath(pathname, opts) {
|
|
40
40
|
return activePipeline ? activePipeline.revalidatePath(pathname, opts) : 0;
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
function registerTwmTagInvalidator(fn) {
|
|
43
|
+
twmTagInvalidator = fn;
|
|
44
|
+
}
|
|
45
|
+
function revalidateTag(tag) {
|
|
46
|
+
let dropped = 0;
|
|
47
|
+
if (activePipeline) dropped += activePipeline.revalidateTag(tag);
|
|
48
|
+
if (twmTagInvalidator) dropped += twmTagInvalidator(tag);
|
|
49
|
+
return dropped;
|
|
50
|
+
}
|
|
51
|
+
var activePipeline, twmTagInvalidator;
|
|
43
52
|
var init_revalidate = __esm({
|
|
44
53
|
"../../packages/server/tw/routing/revalidate.ts"() {
|
|
45
54
|
activePipeline = null;
|
|
55
|
+
twmTagInvalidator = null;
|
|
46
56
|
}
|
|
47
57
|
});
|
|
48
58
|
|
|
@@ -627,905 +637,486 @@ var init_twm_rules = __esm({
|
|
|
627
637
|
}
|
|
628
638
|
});
|
|
629
639
|
|
|
630
|
-
// ../../packages/shared/tw/
|
|
631
|
-
var
|
|
632
|
-
|
|
633
|
-
twImportTs: () => twImportTs
|
|
634
|
-
});
|
|
635
|
-
async function twImportTs(tsPath) {
|
|
636
|
-
if (!globalThis.__TW_NODE) {
|
|
637
|
-
return import(tsPath);
|
|
640
|
+
// ../../packages/shared/tw/hashing/md5.ts
|
|
641
|
+
var init_md5 = __esm({
|
|
642
|
+
"../../packages/shared/tw/hashing/md5.ts"() {
|
|
638
643
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
"../../packages/shared/tw/node-import.ts"() {
|
|
657
|
-
import_node_path = require("node:path");
|
|
658
|
-
import_node_os = require("node:os");
|
|
659
|
-
import_node_url = require("node:url");
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
// ../../packages/shared/tw/hashing/crc32.ts
|
|
647
|
+
var crc32Table;
|
|
648
|
+
var init_crc32 = __esm({
|
|
649
|
+
"../../packages/shared/tw/hashing/crc32.ts"() {
|
|
650
|
+
crc32Table = (() => {
|
|
651
|
+
const table = new Uint32Array(256);
|
|
652
|
+
for (let i = 0; i < 256; i++) {
|
|
653
|
+
let crc = i;
|
|
654
|
+
for (let j = 0; j < 8; j++) {
|
|
655
|
+
crc = crc & 1 ? 3988292384 ^ crc >>> 1 : crc >>> 1;
|
|
656
|
+
}
|
|
657
|
+
table[i] = crc >>> 0;
|
|
658
|
+
}
|
|
659
|
+
return table;
|
|
660
|
+
})();
|
|
660
661
|
}
|
|
661
662
|
});
|
|
662
663
|
|
|
663
|
-
// ../../packages/
|
|
664
|
-
function
|
|
665
|
-
const
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
return
|
|
664
|
+
// ../../packages/shared/tw/hashing/sha.ts
|
|
665
|
+
function syncHashFallback(algorithm, data) {
|
|
666
|
+
const input = typeof data === "string" ? data : new TextDecoder().decode(data);
|
|
667
|
+
let hash2 = 2166136261;
|
|
668
|
+
for (let i = 0; i < input.length; i++) {
|
|
669
|
+
hash2 ^= input.charCodeAt(i);
|
|
670
|
+
hash2 = Math.imul(hash2, 16777619);
|
|
671
|
+
}
|
|
672
|
+
return (hash2 >>> 0).toString(16).padStart(8, "0").repeat(8);
|
|
672
673
|
}
|
|
673
|
-
function
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
const srcset = widths.map(
|
|
681
|
-
(bp) => isRemote ? `/_tw/img/_remote?src=${encodeURIComponent(opts.src)}&w=${bp}&q=${quality}&f=auto ${bp}w` : `${optimizedImageUrl(opts.src, { width: bp, quality })} ${bp}w`
|
|
682
|
-
).join(", ");
|
|
683
|
-
const sizes = width > 0 ? `(max-width: ${width}px) 100vw, ${width}px` : "100vw";
|
|
684
|
-
const loading = opts.priority ? "eager" : "lazy";
|
|
685
|
-
return { src, srcset, sizes, width, height: opts.height ?? 0, loading, decoding: "async", format };
|
|
674
|
+
function sha256(data) {
|
|
675
|
+
try {
|
|
676
|
+
const hasher = new BunHashSync("sha256");
|
|
677
|
+
return hasher.update(data).digest("hex");
|
|
678
|
+
} catch {
|
|
679
|
+
return syncHashFallback("sha256", data);
|
|
680
|
+
}
|
|
686
681
|
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
req = createRequire((0, import_node_path2.join)(rootDir, "package.json"));
|
|
701
|
-
} catch {
|
|
702
|
-
}
|
|
703
|
-
const candidates = [
|
|
704
|
-
(0, import_node_path2.join)(rootDir, "node_modules", "sharp"),
|
|
705
|
-
"sharp"
|
|
706
|
-
];
|
|
707
|
-
for (const candidate of candidates) {
|
|
708
|
-
if (typeof req === "function") {
|
|
709
|
-
try {
|
|
710
|
-
const m = req(candidate);
|
|
711
|
-
const mod = m?.default ?? m;
|
|
712
|
-
if (mod && typeof mod === "function") return mod;
|
|
713
|
-
} catch {
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
try {
|
|
717
|
-
const href = candidate.startsWith("/") ? (0, import_node_url2.pathToFileURL)(candidate).href : candidate;
|
|
718
|
-
const m = await import(href);
|
|
719
|
-
const mod = m?.default ?? m;
|
|
720
|
-
if (mod && typeof mod === "function") return mod;
|
|
721
|
-
} catch {
|
|
722
|
-
}
|
|
682
|
+
var BunHashSync;
|
|
683
|
+
var init_sha = __esm({
|
|
684
|
+
"../../packages/shared/tw/hashing/sha.ts"() {
|
|
685
|
+
init_md5();
|
|
686
|
+
init_crc32();
|
|
687
|
+
BunHashSync = class {
|
|
688
|
+
hasher;
|
|
689
|
+
constructor(algorithm) {
|
|
690
|
+
const nodeCrypto = globalThis.require?.("node:crypto") || globalThis.crypto;
|
|
691
|
+
if (nodeCrypto?.createHash) {
|
|
692
|
+
this.hasher = nodeCrypto.createHash(algorithm);
|
|
693
|
+
} else {
|
|
694
|
+
throw new Error("No sync hash available");
|
|
723
695
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
696
|
+
}
|
|
697
|
+
update(data) {
|
|
698
|
+
const input = typeof data === "string" ? data : Buffer.from(data);
|
|
699
|
+
this.hasher.update(input);
|
|
700
|
+
return this;
|
|
701
|
+
}
|
|
702
|
+
digest(encoding) {
|
|
703
|
+
return this.hasher.digest(encoding);
|
|
704
|
+
}
|
|
705
|
+
};
|
|
728
706
|
}
|
|
729
|
-
|
|
730
|
-
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
// ../../packages/shared/tw/utils.ts
|
|
710
|
+
var init_utils = __esm({
|
|
711
|
+
"../../packages/shared/tw/utils.ts"() {
|
|
731
712
|
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
// ../../packages/shared/tw/fs/runtime-compat.ts
|
|
716
|
+
var init_runtime_compat = __esm({
|
|
717
|
+
"../../packages/shared/tw/fs/runtime-compat.ts"() {
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
// ../../packages/shared/tw/constants/misc.ts
|
|
722
|
+
var MIME_TYPES, VLQ_BASE_SHIFT, VLQ_BASE, VLQ_BASE_MASK;
|
|
723
|
+
var init_misc = __esm({
|
|
724
|
+
"../../packages/shared/tw/constants/misc.ts"() {
|
|
725
|
+
MIME_TYPES = {
|
|
726
|
+
".html": "text/html",
|
|
727
|
+
".htm": "text/html",
|
|
728
|
+
".css": "text/css",
|
|
729
|
+
".js": "text/javascript",
|
|
730
|
+
".mjs": "text/javascript",
|
|
731
|
+
".json": "application/json",
|
|
732
|
+
".xml": "application/xml",
|
|
733
|
+
".txt": "text/plain",
|
|
734
|
+
".md": "text/markdown",
|
|
735
|
+
".csv": "text/csv",
|
|
736
|
+
".svg": "image/svg+xml",
|
|
737
|
+
".png": "image/png",
|
|
738
|
+
".jpg": "image/jpeg",
|
|
739
|
+
".jpeg": "image/jpeg",
|
|
740
|
+
".gif": "image/gif",
|
|
741
|
+
".webp": "image/webp",
|
|
742
|
+
".avif": "image/avif",
|
|
743
|
+
".ico": "image/x-icon",
|
|
744
|
+
".bmp": "image/bmp",
|
|
745
|
+
".woff": "font/woff",
|
|
746
|
+
".woff2": "font/woff2",
|
|
747
|
+
".ttf": "font/ttf",
|
|
748
|
+
".otf": "font/otf",
|
|
749
|
+
".pdf": "application/pdf",
|
|
750
|
+
".zip": "application/zip",
|
|
751
|
+
".gz": "application/gzip",
|
|
752
|
+
".tar": "application/x-tar",
|
|
753
|
+
".mp4": "video/mp4",
|
|
754
|
+
".webm": "video/webm",
|
|
755
|
+
".mp3": "audio/mpeg",
|
|
756
|
+
".ogg": "audio/ogg",
|
|
757
|
+
".wav": "audio/wav",
|
|
758
|
+
".wasm": "application/wasm",
|
|
759
|
+
".tw": "text/tw",
|
|
760
|
+
".tss": "text/tss",
|
|
761
|
+
".toml": "application/toml",
|
|
762
|
+
".yaml": "application/yaml",
|
|
763
|
+
".yml": "application/yaml",
|
|
764
|
+
".webmanifest": "application/manifest+json"
|
|
765
|
+
};
|
|
766
|
+
VLQ_BASE_SHIFT = 5;
|
|
767
|
+
VLQ_BASE = 1 << VLQ_BASE_SHIFT;
|
|
768
|
+
VLQ_BASE_MASK = VLQ_BASE - 1;
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
// ../../packages/shared/tw/fs/async.ts
|
|
773
|
+
var init_async = __esm({
|
|
774
|
+
"../../packages/shared/tw/fs/async.ts"() {
|
|
775
|
+
init_runtime_compat();
|
|
776
|
+
init_runtime_compat();
|
|
777
|
+
init_misc();
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
// ../../packages/runtime/tw/utils/path-utils.ts
|
|
782
|
+
var init_path_utils = __esm({
|
|
783
|
+
"../../packages/runtime/tw/utils/path-utils.ts"() {
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
// ../../packages/shared/tw/fs/glob.ts
|
|
788
|
+
var init_glob = __esm({
|
|
789
|
+
"../../packages/shared/tw/fs/glob.ts"() {
|
|
790
|
+
init_runtime_compat();
|
|
791
|
+
init_path_utils();
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
// ../../packages/shared/tw/fs/sync.ts
|
|
796
|
+
var init_sync = __esm({
|
|
797
|
+
"../../packages/shared/tw/fs/sync.ts"() {
|
|
798
|
+
init_runtime_compat();
|
|
799
|
+
init_runtime_compat();
|
|
800
|
+
init_async();
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
// ../../packages/shared/tw/fs/watch.ts
|
|
805
|
+
var init_watch = __esm({
|
|
806
|
+
"../../packages/shared/tw/fs/watch.ts"() {
|
|
807
|
+
init_sync();
|
|
808
|
+
init_runtime_compat();
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
// ../../packages/shared/tw/fs/index.ts
|
|
813
|
+
var init_fs = __esm({
|
|
814
|
+
"../../packages/shared/tw/fs/index.ts"() {
|
|
815
|
+
init_async();
|
|
816
|
+
init_glob();
|
|
817
|
+
init_sync();
|
|
818
|
+
init_watch();
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
// ../../packages/shared/tw/config/defaults.ts
|
|
823
|
+
var init_defaults = __esm({
|
|
824
|
+
"../../packages/shared/tw/config/defaults.ts"() {
|
|
825
|
+
init_fs();
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
// ../../packages/shared/tw/logger/levels.ts
|
|
830
|
+
var init_levels = __esm({
|
|
831
|
+
"../../packages/shared/tw/logger/levels.ts"() {
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
// ../../packages/shared/tw/logger/colors.ts
|
|
836
|
+
var ANSI, LEVEL_COLORS, LEVEL_ICONS;
|
|
837
|
+
var init_colors = __esm({
|
|
838
|
+
"../../packages/shared/tw/logger/colors.ts"() {
|
|
839
|
+
init_levels();
|
|
840
|
+
ANSI = {
|
|
841
|
+
reset: "\x1B[0m",
|
|
842
|
+
bold: "\x1B[1m",
|
|
843
|
+
dim: "\x1B[2m",
|
|
844
|
+
italic: "\x1B[3m",
|
|
845
|
+
underline: "\x1B[4m",
|
|
846
|
+
blink: "\x1B[5m",
|
|
847
|
+
inverse: "\x1B[7m",
|
|
848
|
+
hidden: "\x1B[8m",
|
|
849
|
+
strikethrough: "\x1B[9m",
|
|
850
|
+
black: "\x1B[30m",
|
|
851
|
+
red: "\x1B[31m",
|
|
852
|
+
green: "\x1B[32m",
|
|
853
|
+
yellow: "\x1B[33m",
|
|
854
|
+
blue: "\x1B[34m",
|
|
855
|
+
magenta: "\x1B[35m",
|
|
856
|
+
cyan: "\x1B[36m",
|
|
857
|
+
white: "\x1B[37m",
|
|
858
|
+
bgBlack: "\x1B[40m",
|
|
859
|
+
bgRed: "\x1B[41m",
|
|
860
|
+
bgGreen: "\x1B[42m",
|
|
861
|
+
bgYellow: "\x1B[44m",
|
|
862
|
+
bgBlue: "\x1B[44m",
|
|
863
|
+
bgMagenta: "\x1B[45m",
|
|
864
|
+
bgCyan: "\x1B[46m",
|
|
865
|
+
bgWhite: "\x1B[47m",
|
|
866
|
+
brightBlack: "\x1B[90m",
|
|
867
|
+
brightRed: "\x1B[91m",
|
|
868
|
+
brightGreen: "\x1B[92m",
|
|
869
|
+
brightYellow: "\x1B[93m",
|
|
870
|
+
brightBlue: "\x1B[94m",
|
|
871
|
+
brightMagenta: "\x1B[95m",
|
|
872
|
+
brightCyan: "\x1B[96m",
|
|
873
|
+
brightWhite: "\x1B[97m"
|
|
874
|
+
};
|
|
875
|
+
LEVEL_COLORS = {
|
|
876
|
+
[0 /* SILENT */]: ANSI.dim,
|
|
877
|
+
[1 /* ERROR */]: ANSI.red,
|
|
878
|
+
[2 /* WARN */]: ANSI.yellow,
|
|
879
|
+
[3 /* INFO */]: ANSI.blue,
|
|
880
|
+
[4 /* DEBUG */]: ANSI.magenta,
|
|
881
|
+
[5 /* TRACE */]: ANSI.dim
|
|
882
|
+
};
|
|
883
|
+
LEVEL_ICONS = {
|
|
884
|
+
[0 /* SILENT */]: "",
|
|
885
|
+
[1 /* ERROR */]: "?",
|
|
886
|
+
[2 /* WARN */]: "?",
|
|
887
|
+
[3 /* INFO */]: "?",
|
|
888
|
+
[4 /* DEBUG */]: "o",
|
|
889
|
+
[5 /* TRACE */]: "o"
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
// ../../packages/shared/tw/logger/transports.ts
|
|
895
|
+
var init_transports = __esm({
|
|
896
|
+
"../../packages/shared/tw/logger/transports.ts"() {
|
|
897
|
+
init_colors();
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
// ../../packages/shared/tw/logger/logger.ts
|
|
902
|
+
var init_logger = __esm({
|
|
903
|
+
"../../packages/shared/tw/logger/logger.ts"() {
|
|
904
|
+
init_levels();
|
|
905
|
+
init_transports();
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
// ../../packages/shared/tw/logger/progress.ts
|
|
910
|
+
var init_progress = __esm({
|
|
911
|
+
"../../packages/shared/tw/logger/progress.ts"() {
|
|
912
|
+
init_logger();
|
|
913
|
+
init_colors();
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
// ../../packages/shared/tw/logger/index.ts
|
|
918
|
+
var init_logger2 = __esm({
|
|
919
|
+
"../../packages/shared/tw/logger/index.ts"() {
|
|
920
|
+
init_colors();
|
|
921
|
+
init_levels();
|
|
922
|
+
init_logger();
|
|
923
|
+
init_progress();
|
|
924
|
+
init_transports();
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
// ../../packages/shared/tw/config/validate.ts
|
|
929
|
+
var init_validate = __esm({
|
|
930
|
+
"../../packages/shared/tw/config/validate.ts"() {
|
|
931
|
+
init_logger2();
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
// ../../packages/shared/tw/config/loader.ts
|
|
936
|
+
var init_loader = __esm({
|
|
937
|
+
"../../packages/shared/tw/config/loader.ts"() {
|
|
938
|
+
init_defaults();
|
|
939
|
+
init_fs();
|
|
940
|
+
init_logger2();
|
|
941
|
+
init_validate();
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
// ../../packages/shared/tw/config/index.ts
|
|
946
|
+
var init_config = __esm({
|
|
947
|
+
"../../packages/shared/tw/config/index.ts"() {
|
|
948
|
+
init_defaults();
|
|
949
|
+
init_loader();
|
|
950
|
+
init_validate();
|
|
951
|
+
}
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
// ../../packages/shared/tw/cache.ts
|
|
955
|
+
function parseCacheBody(body) {
|
|
956
|
+
const meta = {};
|
|
957
|
+
const pairRegex = /\b(life|tag|revalidate|stale|expire)\s*=?\s*(?:"([^"]*)"|'([^']*)'|(\d+))/g;
|
|
958
|
+
let m;
|
|
959
|
+
while ((m = pairRegex.exec(body)) !== null) {
|
|
960
|
+
const key = m[1];
|
|
961
|
+
const strVal = m[2] ?? m[3] ?? null;
|
|
962
|
+
const numVal = m[4] != null ? Number(m[4]) : null;
|
|
963
|
+
if (key === "revalidate" || key === "stale" || key === "expire") {
|
|
964
|
+
if (numVal != null && Number.isFinite(numVal) && numVal >= 0) {
|
|
965
|
+
meta[key] = numVal;
|
|
966
|
+
}
|
|
967
|
+
} else if (key === "life" || key === "tag") {
|
|
968
|
+
if (strVal != null && strVal.length > 0) {
|
|
969
|
+
meta[key] = strVal;
|
|
970
|
+
}
|
|
735
971
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
972
|
+
}
|
|
973
|
+
return meta;
|
|
974
|
+
}
|
|
975
|
+
function extractCacheDirective(source) {
|
|
976
|
+
const m = /page\s*\{[^}]*?\bcache\s*\{([^}]*)\}/.exec(source);
|
|
977
|
+
if (!m) return null;
|
|
978
|
+
return parseCacheBody(m[1]);
|
|
979
|
+
}
|
|
980
|
+
function cacheMetaIsUsable(meta) {
|
|
981
|
+
if (!meta) return false;
|
|
982
|
+
return meta.revalidate != null || meta.stale != null || meta.expire != null || !!meta.life;
|
|
983
|
+
}
|
|
984
|
+
function resolveCache(meta, profiles, opts) {
|
|
985
|
+
if (!cacheMetaIsUsable(meta)) return null;
|
|
986
|
+
let revalidate = meta.revalidate;
|
|
987
|
+
let stale = meta.stale ?? 0;
|
|
988
|
+
let expire = meta.expire;
|
|
989
|
+
if (meta.life) {
|
|
990
|
+
const profile = (profiles && profiles[meta.life]) ?? BUILTIN_CACHE_PROFILES[meta.life];
|
|
991
|
+
if (!profile) {
|
|
992
|
+
throw new Error(
|
|
993
|
+
'TW092: Unknown cache profile "' + meta.life + '". Add it to tw.config.ts cache.profiles or use one of: ' + Object.keys(BUILTIN_CACHE_PROFILES).join(", ") + "."
|
|
994
|
+
);
|
|
739
995
|
}
|
|
740
|
-
|
|
996
|
+
if (revalidate == null && profile.revalidate != null) revalidate = profile.revalidate;
|
|
997
|
+
if (meta.stale == null && profile.stale != null) stale = profile.stale;
|
|
998
|
+
if (expire == null && profile.expire != null) expire = profile.expire;
|
|
741
999
|
}
|
|
742
|
-
|
|
743
|
-
|
|
1000
|
+
if (revalidate == null || !Number.isFinite(revalidate) || revalidate <= 0) return null;
|
|
1001
|
+
if (expire == null || !Number.isFinite(expire)) {
|
|
1002
|
+
expire = opts?.legacy ? Infinity : revalidate;
|
|
744
1003
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
1004
|
+
if (expire < revalidate) expire = revalidate;
|
|
1005
|
+
return { revalidate, stale, expire, tag: meta.tag };
|
|
1006
|
+
}
|
|
1007
|
+
function canonicalizeQuery(search) {
|
|
1008
|
+
if (!search) return "";
|
|
1009
|
+
const q = search.startsWith("?") ? search.slice(1) : search;
|
|
1010
|
+
if (!q) return "";
|
|
1011
|
+
const pairs = [];
|
|
1012
|
+
for (const part of q.split("&")) {
|
|
1013
|
+
if (!part) continue;
|
|
1014
|
+
const i = part.indexOf("=");
|
|
1015
|
+
let k, v;
|
|
1016
|
+
if (i < 0) {
|
|
1017
|
+
k = part;
|
|
1018
|
+
v = "";
|
|
1019
|
+
} else {
|
|
1020
|
+
k = part.slice(0, i);
|
|
1021
|
+
v = part.slice(i + 1);
|
|
1022
|
+
}
|
|
1023
|
+
if (v === "") continue;
|
|
1024
|
+
try {
|
|
1025
|
+
k = decodeURIComponent(k);
|
|
1026
|
+
} catch {
|
|
1027
|
+
}
|
|
1028
|
+
try {
|
|
1029
|
+
v = decodeURIComponent(v);
|
|
1030
|
+
} catch {
|
|
1031
|
+
}
|
|
1032
|
+
pairs.push([k, v]);
|
|
749
1033
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
1034
|
+
pairs.sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
1035
|
+
return pairs.map(([k, v]) => encodeURIComponent(k) + "=" + encodeURIComponent(v)).join("&");
|
|
1036
|
+
}
|
|
1037
|
+
function balancedEnd(source, i) {
|
|
1038
|
+
let depth = 0;
|
|
1039
|
+
let inStr = null;
|
|
1040
|
+
for (let k = i; k < source.length; k++) {
|
|
1041
|
+
const ch = source[k];
|
|
1042
|
+
if (inStr) {
|
|
1043
|
+
if (ch === "\\") k++;
|
|
1044
|
+
else if (ch === inStr) inStr = null;
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
1048
|
+
inStr = ch;
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
1051
|
+
if (ch === "{") depth++;
|
|
1052
|
+
else if (ch === "}") {
|
|
1053
|
+
depth--;
|
|
1054
|
+
if (depth === 0) return k;
|
|
760
1055
|
}
|
|
761
|
-
const h = host.toLowerCase();
|
|
762
|
-
if (h === "::" || h === "::1") return true;
|
|
763
|
-
if (h.startsWith("fe80:")) return true;
|
|
764
|
-
if (h.startsWith("fc") || h.startsWith("fd")) return true;
|
|
765
|
-
if (h.startsWith("::ffff:")) return isPrivateAddress(h.slice(7));
|
|
766
|
-
return false;
|
|
767
1056
|
}
|
|
768
|
-
|
|
769
|
-
|
|
1057
|
+
return -1;
|
|
1058
|
+
}
|
|
1059
|
+
function readCacheProfilesSync(rootDir) {
|
|
1060
|
+
try {
|
|
1061
|
+
let read;
|
|
770
1062
|
try {
|
|
771
|
-
|
|
1063
|
+
read = globalThis.require("node:fs").readFileSync;
|
|
772
1064
|
} catch {
|
|
773
|
-
|
|
1065
|
+
const fsmod = globalThis.process ? globalThis.process.getBuiltinModule?.("node:fs") : null;
|
|
1066
|
+
if (!fsmod) return {};
|
|
1067
|
+
read = fsmod.readFileSync;
|
|
774
1068
|
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
try {
|
|
778
|
-
const dns = await import("node:dns");
|
|
779
|
-
const addrs = await dns.promises.lookup(url.hostname, { all: true });
|
|
780
|
-
if (!addrs.length) return null;
|
|
781
|
-
for (const a of addrs) {
|
|
782
|
-
if (isPrivateAddress(a.address)) return null;
|
|
783
|
-
}
|
|
784
|
-
} catch {
|
|
785
|
-
return null;
|
|
786
|
-
}
|
|
787
|
-
let res;
|
|
1069
|
+
let src = "";
|
|
1070
|
+
for (const name of ["tw.config.ts", "tw.config.js", "tw.config.mjs"]) {
|
|
788
1071
|
try {
|
|
789
|
-
|
|
1072
|
+
src = read(rootDir.endsWith("/") ? rootDir + name : rootDir + "/" + name, "utf-8");
|
|
1073
|
+
if (src) break;
|
|
790
1074
|
} catch {
|
|
791
|
-
return null;
|
|
792
|
-
}
|
|
793
|
-
if (res.status >= 300 && res.status < 400) {
|
|
794
|
-
const loc = res.headers.get("location");
|
|
795
|
-
if (!loc) return null;
|
|
796
|
-
try {
|
|
797
|
-
url = new URL(loc, url);
|
|
798
|
-
} catch {
|
|
799
|
-
return null;
|
|
800
|
-
}
|
|
801
|
-
continue;
|
|
802
1075
|
}
|
|
803
|
-
if (!res.ok) return null;
|
|
804
|
-
return Buffer.from(await res.arrayBuffer());
|
|
805
1076
|
}
|
|
806
|
-
return
|
|
1077
|
+
if (!src) return {};
|
|
1078
|
+
return parseCacheProfilesSource(src);
|
|
1079
|
+
} catch {
|
|
1080
|
+
return {};
|
|
807
1081
|
}
|
|
808
|
-
async function readSource(src) {
|
|
809
|
-
if (isRemoteSource(src)) {
|
|
810
|
-
const data = await safeFetchRemote(src);
|
|
811
|
-
return data ? { data, mtime: 0 } : null;
|
|
812
|
-
}
|
|
813
|
-
let decoded;
|
|
814
|
-
try {
|
|
815
|
-
decoded = decodeURIComponent(src);
|
|
816
|
-
} catch {
|
|
817
|
-
return null;
|
|
818
|
-
}
|
|
819
|
-
const clean = (0, import_node_path2.normalize)(decoded).replace(/\\/g, "/");
|
|
820
|
-
if (clean.includes("\0") || clean.includes("..")) return null;
|
|
821
|
-
const file = (0, import_node_path2.resolve)(sourceDir, "." + (clean.startsWith("/") ? clean : "/" + clean));
|
|
822
|
-
if (!file.startsWith(sourceDir + "/") && file !== sourceDir) return null;
|
|
823
|
-
const dot = clean.split("/").pop() ?? "";
|
|
824
|
-
if (dot.startsWith(".")) return null;
|
|
825
|
-
if (!(0, import_node_fs.existsSync)(file)) return null;
|
|
826
|
-
const st = (0, import_node_fs.statSync)(file);
|
|
827
|
-
if (!st.isFile()) return null;
|
|
828
|
-
return { data: (0, import_node_fs.readFileSync)(file), mtime: st.mtimeMs };
|
|
829
|
-
}
|
|
830
|
-
function cacheKey(src, w, h, q, f, mtime) {
|
|
831
|
-
return (0, import_node_crypto2.createHash)("sha256").update(`${src}|${mtime}|${w}|${h}|${q}|${f}`).digest("hex").slice(0, 32);
|
|
832
|
-
}
|
|
833
|
-
async function processBuffer(data, w, h, q, format) {
|
|
834
|
-
const sharp = await loadSharp();
|
|
835
|
-
if (sharp && format) {
|
|
836
|
-
try {
|
|
837
|
-
let img = sharp(data);
|
|
838
|
-
if (w || h) {
|
|
839
|
-
img = img.resize(w || void 0, h || void 0, {
|
|
840
|
-
fit: "inside",
|
|
841
|
-
withoutEnlargement: true
|
|
842
|
-
});
|
|
843
|
-
}
|
|
844
|
-
if (format !== "jpeg" && format !== "png") {
|
|
845
|
-
img = img.toFormat(format, { quality: q });
|
|
846
|
-
} else if (format === "jpeg") {
|
|
847
|
-
img = img.jpeg({ quality: q });
|
|
848
|
-
}
|
|
849
|
-
const out = await img.toBuffer();
|
|
850
|
-
return { data: out, contentType: `image/${format}`, viaSharp: true };
|
|
851
|
-
} catch (err2) {
|
|
852
|
-
console.warn(` [tw/image] processing failed (${format}, ${w}x${h}), serving original: ${err2?.message ?? err2}`);
|
|
853
|
-
return { data, contentType: "", viaSharp: false };
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
if (!warnedNoSharp) {
|
|
857
|
-
warnedNoSharp = true;
|
|
858
|
-
console.warn(" [tw/image] sharp not installed \u2014 serving original images (bun add sharp to enable resizing/WebP/AVIF)");
|
|
859
|
-
}
|
|
860
|
-
return { data, contentType: "", viaSharp: false };
|
|
861
|
-
}
|
|
862
|
-
async function handle(request) {
|
|
863
|
-
const url = new URL(request.url);
|
|
864
|
-
const pathname = url.pathname;
|
|
865
|
-
if (!pathname.startsWith("/_tw/img/") && pathname !== "/_tw/img") return null;
|
|
866
|
-
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
867
|
-
return new Response("Method Not Allowed", { status: 405 });
|
|
868
|
-
}
|
|
869
|
-
const q = url.searchParams;
|
|
870
|
-
let src;
|
|
871
|
-
if (pathname === "/_tw/img/_remote") {
|
|
872
|
-
src = q.get("src") ?? "";
|
|
873
|
-
if (!src) return new Response("Not Found", { status: 404 });
|
|
874
|
-
} else {
|
|
875
|
-
src = pathname.slice("/_tw/img".length) || "/";
|
|
876
|
-
if (src === "/" || src === "") return new Response("Not Found", { status: 404 });
|
|
877
|
-
}
|
|
878
|
-
const w = Math.max(0, Math.min(1e4, parseInt(q.get("w") ?? "0", 10) || 0));
|
|
879
|
-
const h = Math.max(0, Math.min(1e4, parseInt(q.get("h") ?? "0", 10) || 0));
|
|
880
|
-
const quality = Math.max(1, Math.min(100, parseInt(q.get("q") ?? String(config.quality), 10) || config.quality));
|
|
881
|
-
const format = negotiateFormat(q.get("f"), request.headers.get("accept") ?? "");
|
|
882
|
-
const source = await readSource(src);
|
|
883
|
-
if (!source) return new Response("Not Found", { status: 404 });
|
|
884
|
-
if ((0, import_node_path2.extname)(src).toLowerCase() === ".svg") {
|
|
885
|
-
return new Response(new Uint8Array(source.data), {
|
|
886
|
-
status: 200,
|
|
887
|
-
headers: {
|
|
888
|
-
"Content-Type": "image/svg+xml",
|
|
889
|
-
"Cache-Control": `public, max-age=${config.cacheMaxAge}`,
|
|
890
|
-
"X-TW-Image": "svg-passthrough"
|
|
891
|
-
}
|
|
892
|
-
});
|
|
893
|
-
}
|
|
894
|
-
if (!w && !h && !format) {
|
|
895
|
-
return new Response(new Uint8Array(source.data), {
|
|
896
|
-
status: 200,
|
|
897
|
-
headers: {
|
|
898
|
-
"Content-Type": contentTypeFor(src),
|
|
899
|
-
"Cache-Control": `public, max-age=${config.cacheMaxAge}`,
|
|
900
|
-
"X-TW-Image": "original"
|
|
901
|
-
}
|
|
902
|
-
});
|
|
903
|
-
}
|
|
904
|
-
const key = cacheKey(src, w, h, quality, format ?? "orig", source.mtime);
|
|
905
|
-
const cacheFile = (0, import_node_path2.join)(cacheDir, `${key}${format ? "." + format : ""}`);
|
|
906
|
-
const etag = `"${key}"`;
|
|
907
|
-
if (request.headers.get("if-none-match") === etag) {
|
|
908
|
-
return new Response(null, { status: 304, headers: { ETag: etag } });
|
|
909
|
-
}
|
|
910
|
-
if ((0, import_node_fs.existsSync)(cacheFile)) {
|
|
911
|
-
const cached = (0, import_node_fs.readFileSync)(cacheFile);
|
|
912
|
-
return new Response(new Uint8Array(cached), {
|
|
913
|
-
status: 200,
|
|
914
|
-
headers: {
|
|
915
|
-
"Content-Type": contentTypeFor(cacheFile),
|
|
916
|
-
"Cache-Control": `public, max-age=${config.cacheMaxAge}`,
|
|
917
|
-
ETag: etag,
|
|
918
|
-
"X-TW-Image": "cache"
|
|
919
|
-
}
|
|
920
|
-
});
|
|
921
|
-
}
|
|
922
|
-
const result = await processBuffer(source.data, w, h, quality, format);
|
|
923
|
-
if (result.viaSharp) {
|
|
924
|
-
try {
|
|
925
|
-
(0, import_node_fs.mkdirSync)(cacheDir, { recursive: true });
|
|
926
|
-
(0, import_node_fs.writeFileSync)(cacheFile, result.data);
|
|
927
|
-
} catch {
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
return new Response(new Uint8Array(result.data), {
|
|
931
|
-
status: 200,
|
|
932
|
-
headers: {
|
|
933
|
-
"Content-Type": result.contentType || contentTypeFor(src),
|
|
934
|
-
"Cache-Control": `public, max-age=${config.cacheMaxAge}`,
|
|
935
|
-
ETag: etag,
|
|
936
|
-
"X-TW-Image": result.viaSharp ? "optimized" : "passthrough"
|
|
937
|
-
}
|
|
938
|
-
});
|
|
939
|
-
}
|
|
940
|
-
return { handle, sourceDir, cacheDir, config };
|
|
941
|
-
}
|
|
942
|
-
var import_node_crypto2, import_node_fs, import_node_path2, import_node_url2, DEFAULT_IMAGE_CONFIG, IMAGE_EXTENSIONS, SHARP_FORMATS;
|
|
943
|
-
var init_tw = __esm({
|
|
944
|
-
"../../packages/image/tw/index.ts"() {
|
|
945
|
-
import_node_crypto2 = require("node:crypto");
|
|
946
|
-
import_node_fs = require("node:fs");
|
|
947
|
-
import_node_path2 = require("node:path");
|
|
948
|
-
import_node_url2 = require("node:url");
|
|
949
|
-
DEFAULT_IMAGE_CONFIG = {
|
|
950
|
-
quality: 75,
|
|
951
|
-
formats: ["avif", "webp"],
|
|
952
|
-
breakpoints: [320, 640, 768, 1024, 1280, 1920],
|
|
953
|
-
remoteAllowHosts: [],
|
|
954
|
-
cacheMaxAge: 86400
|
|
955
|
-
};
|
|
956
|
-
IMAGE_EXTENSIONS = {
|
|
957
|
-
".jpg": "image/jpeg",
|
|
958
|
-
".jpeg": "image/jpeg",
|
|
959
|
-
".png": "image/png",
|
|
960
|
-
".webp": "image/webp",
|
|
961
|
-
".avif": "image/avif",
|
|
962
|
-
".gif": "image/gif",
|
|
963
|
-
".svg": "image/svg+xml",
|
|
964
|
-
".ico": "image/x-icon"
|
|
965
|
-
};
|
|
966
|
-
SHARP_FORMATS = {
|
|
967
|
-
avif: "avif",
|
|
968
|
-
webp: "webp",
|
|
969
|
-
jpeg: "jpeg",
|
|
970
|
-
jpg: "jpeg",
|
|
971
|
-
png: "png"
|
|
972
|
-
};
|
|
973
|
-
}
|
|
974
|
-
});
|
|
975
|
-
|
|
976
|
-
// ../../packages/server/tw/websocket-manager.ts
|
|
977
|
-
var DEFAULT_MAX_ROOMS, DEFAULT_MAX_ROOM_SIZE, WebSocketManager;
|
|
978
|
-
var init_websocket_manager = __esm({
|
|
979
|
-
"../../packages/server/tw/websocket-manager.ts"() {
|
|
980
|
-
DEFAULT_MAX_ROOMS = 1e3;
|
|
981
|
-
DEFAULT_MAX_ROOM_SIZE = 1e4;
|
|
982
|
-
WebSocketManager = class {
|
|
983
|
-
connections = /* @__PURE__ */ new Map();
|
|
984
|
-
meta = /* @__PURE__ */ new Map();
|
|
985
|
-
rooms = /* @__PURE__ */ new Map();
|
|
986
|
-
nextId = 1;
|
|
987
|
-
maxRooms;
|
|
988
|
-
maxRoomSize;
|
|
989
|
-
constructor(options = {}) {
|
|
990
|
-
this.maxRooms = options.maxRooms ?? DEFAULT_MAX_ROOMS;
|
|
991
|
-
this.maxRoomSize = options.maxRoomSize ?? DEFAULT_MAX_ROOM_SIZE;
|
|
992
|
-
}
|
|
993
|
-
/** Register a socket on open; returns the assigned connection id. */
|
|
994
|
-
add(connection, meta = {}) {
|
|
995
|
-
let id = connection?.data?.id ?? connection?.id;
|
|
996
|
-
if (!id) {
|
|
997
|
-
id = "ws-" + process.pid + "-" + this.nextId++;
|
|
998
|
-
}
|
|
999
|
-
if (connection && typeof connection === "object") {
|
|
1000
|
-
if (!connection.data) connection.data = { id };
|
|
1001
|
-
else if (!connection.data.id) connection.data.id = id;
|
|
1002
|
-
if (!connection.id) connection.id = id;
|
|
1003
|
-
}
|
|
1004
|
-
this.connections.set(id, connection);
|
|
1005
|
-
this.meta.set(id, meta);
|
|
1006
|
-
return id;
|
|
1007
|
-
}
|
|
1008
|
-
/** Lookup by connection id. */
|
|
1009
|
-
get(id) {
|
|
1010
|
-
return this.connections.get(id);
|
|
1011
|
-
}
|
|
1012
|
-
/** Deregister on close; auto-leaves rooms (last member out kills the room). */
|
|
1013
|
-
remove(id) {
|
|
1014
|
-
for (const room of this.rooms.values()) {
|
|
1015
|
-
room.members.delete(id);
|
|
1016
|
-
if (room.members.size === 0) this.rooms.delete(room.name);
|
|
1017
|
-
}
|
|
1018
|
-
this.meta.delete(id);
|
|
1019
|
-
return this.connections.delete(id);
|
|
1020
|
-
}
|
|
1021
|
-
/** Serialize once and send to every socket regardless of room membership. */
|
|
1022
|
-
broadcast(payload) {
|
|
1023
|
-
const data = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
1024
|
-
for (const conn of this.connections.values()) {
|
|
1025
|
-
this.safeSend(conn, data);
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
/** Send to one connection. */
|
|
1029
|
-
send(id, payload) {
|
|
1030
|
-
const conn = this.connections.get(id);
|
|
1031
|
-
if (!conn) return false;
|
|
1032
|
-
this.safeSend(conn, typeof payload === "string" ? payload : JSON.stringify(payload));
|
|
1033
|
-
return true;
|
|
1034
|
-
}
|
|
1035
|
-
/** All live entries. */
|
|
1036
|
-
getConnections() {
|
|
1037
|
-
return [...this.connections.keys()].map((id) => ({ id, meta: this.meta.get(id) ?? {} }));
|
|
1038
|
-
}
|
|
1039
|
-
/** Explicit pre-creation with metadata (rooms also auto-create on join). */
|
|
1040
|
-
createRoom(name, metadata = {}) {
|
|
1041
|
-
if (this.rooms.has(name)) return;
|
|
1042
|
-
if (this.rooms.size >= this.maxRooms) {
|
|
1043
|
-
throw new Error(`maxRooms reached (${this.maxRooms}): cannot create "${name}"`);
|
|
1044
|
-
}
|
|
1045
|
-
this.rooms.set(name, { name, metadata, members: /* @__PURE__ */ new Set() });
|
|
1046
|
-
}
|
|
1047
|
-
/** Join a room (auto-creates). A full room refuses joins. */
|
|
1048
|
-
joinRoom(connectionId, name) {
|
|
1049
|
-
if (!this.connections.has(connectionId)) return false;
|
|
1050
|
-
let room = this.rooms.get(name);
|
|
1051
|
-
if (!room) {
|
|
1052
|
-
if (this.rooms.size >= this.maxRooms) {
|
|
1053
|
-
throw new Error(`maxRooms reached (${this.maxRooms}): cannot create "${name}"`);
|
|
1054
|
-
}
|
|
1055
|
-
room = { name, metadata: {}, members: /* @__PURE__ */ new Set() };
|
|
1056
|
-
this.rooms.set(name, room);
|
|
1057
|
-
}
|
|
1058
|
-
if (room.members.size >= this.maxRoomSize) return false;
|
|
1059
|
-
room.members.add(connectionId);
|
|
1060
|
-
return true;
|
|
1061
|
-
}
|
|
1062
|
-
/** Leave a room; the room disappears when its last member leaves. */
|
|
1063
|
-
leaveRoom(connectionId, name) {
|
|
1064
|
-
const room = this.rooms.get(name);
|
|
1065
|
-
if (!room) return false;
|
|
1066
|
-
const had = room.members.delete(connectionId);
|
|
1067
|
-
if (room.members.size === 0) this.rooms.delete(name);
|
|
1068
|
-
return had;
|
|
1069
|
-
}
|
|
1070
|
-
/** Iterate only that room's members — Set membership guarantees exactly one copy. */
|
|
1071
|
-
broadcastToRoom(name, payload) {
|
|
1072
|
-
const room = this.rooms.get(name);
|
|
1073
|
-
if (!room) return 0;
|
|
1074
|
-
const data = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
1075
|
-
let sent = 0;
|
|
1076
|
-
for (const id of room.members) {
|
|
1077
|
-
const conn = this.connections.get(id);
|
|
1078
|
-
if (this.safeSend(conn, data)) sent++;
|
|
1079
|
-
}
|
|
1080
|
-
return sent;
|
|
1081
|
-
}
|
|
1082
|
-
/** Room + members. */
|
|
1083
|
-
getRoom(name) {
|
|
1084
|
-
const room = this.rooms.get(name);
|
|
1085
|
-
if (!room) return void 0;
|
|
1086
|
-
return { name: room.name, metadata: room.metadata, members: [...room.members] };
|
|
1087
|
-
}
|
|
1088
|
-
/** All rooms. */
|
|
1089
|
-
getRooms() {
|
|
1090
|
-
return [...this.rooms.values()].map((r) => ({ name: r.name, members: r.members.size }));
|
|
1091
|
-
}
|
|
1092
|
-
safeSend(conn, data) {
|
|
1093
|
-
try {
|
|
1094
|
-
if (!conn || typeof conn?.send !== "function") return false;
|
|
1095
|
-
if (typeof conn.readyState === "number" && conn.readyState !== 1) return false;
|
|
1096
|
-
conn.send(data);
|
|
1097
|
-
return true;
|
|
1098
|
-
} catch {
|
|
1099
|
-
return false;
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
};
|
|
1103
|
-
}
|
|
1104
|
-
});
|
|
1105
|
-
|
|
1106
|
-
// ../../packages/server/tw/routing/layout-chain.ts
|
|
1107
|
-
function resolveLayoutChain(matchResult) {
|
|
1108
|
-
const { node, chain, params } = matchResult;
|
|
1109
|
-
const layouts = [];
|
|
1110
|
-
const templates = [];
|
|
1111
|
-
for (const nodeInChain of chain) {
|
|
1112
|
-
const layoutFile = nodeInChain.files.find((f) => f.type === "layout");
|
|
1113
|
-
if (layoutFile) layouts.push(layoutFile);
|
|
1114
|
-
const templateFile = nodeInChain.files.find((f) => f.type === "template");
|
|
1115
|
-
if (templateFile) templates.push(templateFile);
|
|
1116
|
-
}
|
|
1117
|
-
let loading;
|
|
1118
|
-
let error;
|
|
1119
|
-
let notFound2;
|
|
1120
|
-
let head;
|
|
1121
|
-
for (let i = chain.length - 1; i >= 0; i--) {
|
|
1122
|
-
if (!loading) {
|
|
1123
|
-
loading = chain[i].files.find((f) => f.type === "loading");
|
|
1124
|
-
}
|
|
1125
|
-
if (!error) {
|
|
1126
|
-
error = chain[i].files.find((f) => f.type === "error");
|
|
1127
|
-
}
|
|
1128
|
-
if (!notFound2) {
|
|
1129
|
-
notFound2 = chain[i].files.find((f) => f.type === "not-found");
|
|
1130
|
-
}
|
|
1131
|
-
if (!head) {
|
|
1132
|
-
head = chain[i].files.find((f) => f.type === "head");
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
const page = node.files.find((f) => f.type === "page");
|
|
1136
|
-
let renderMode = "ssr";
|
|
1137
|
-
if (page) {
|
|
1138
|
-
renderMode = extractRenderMode(page);
|
|
1139
|
-
}
|
|
1140
|
-
return {
|
|
1141
|
-
node,
|
|
1142
|
-
chain,
|
|
1143
|
-
params,
|
|
1144
|
-
layouts,
|
|
1145
|
-
templates,
|
|
1146
|
-
loading,
|
|
1147
|
-
error,
|
|
1148
|
-
notFound: notFound2,
|
|
1149
|
-
head,
|
|
1150
|
-
page,
|
|
1151
|
-
middleware: [],
|
|
1152
|
-
renderMode
|
|
1153
|
-
};
|
|
1154
|
-
}
|
|
1155
|
-
function findGlobalError(rootNode) {
|
|
1156
|
-
return rootNode.files.find((f) => f.type === "global-error");
|
|
1157
|
-
}
|
|
1158
|
-
function findRootError(rootNode) {
|
|
1159
|
-
return (rootNode.files || []).find((f) => f.type === "error");
|
|
1160
|
-
}
|
|
1161
|
-
function findRootNotFound(rootNode) {
|
|
1162
|
-
return rootNode.files.find((f) => f.type === "not-found");
|
|
1163
|
-
}
|
|
1164
|
-
function collectParallelDefaults(node) {
|
|
1165
|
-
const defaults = /* @__PURE__ */ new Map();
|
|
1166
|
-
for (const child of node.children) {
|
|
1167
|
-
if (child.kind === "parallel" && child.slotName) {
|
|
1168
|
-
const defaultFile = child.files.find((f) => f.type === "default");
|
|
1169
|
-
if (defaultFile) {
|
|
1170
|
-
defaults.set(child.slotName, defaultFile);
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1174
|
-
return defaults;
|
|
1175
1082
|
}
|
|
1176
|
-
function
|
|
1177
|
-
const
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
}
|
|
1198
|
-
function extractRenderMode(page) {
|
|
1199
|
-
try {
|
|
1200
|
-
const fs = require("node:fs");
|
|
1201
|
-
const source = fs.readFileSync(page.absolutePath, "utf-8");
|
|
1202
|
-
const m = source.match(/render\s+(static|ssr|island|edge)/);
|
|
1203
|
-
if (m) {
|
|
1204
|
-
return m[1];
|
|
1205
|
-
}
|
|
1206
|
-
} catch {
|
|
1207
|
-
}
|
|
1208
|
-
return "ssr";
|
|
1209
|
-
}
|
|
1210
|
-
var init_layout_chain = __esm({
|
|
1211
|
-
"../../packages/server/tw/routing/layout-chain.ts"() {
|
|
1212
|
-
}
|
|
1213
|
-
});
|
|
1214
|
-
|
|
1215
|
-
// ../../packages/shared/tw/hashing/md5.ts
|
|
1216
|
-
var init_md5 = __esm({
|
|
1217
|
-
"../../packages/shared/tw/hashing/md5.ts"() {
|
|
1218
|
-
}
|
|
1219
|
-
});
|
|
1220
|
-
|
|
1221
|
-
// ../../packages/shared/tw/hashing/crc32.ts
|
|
1222
|
-
var crc32Table;
|
|
1223
|
-
var init_crc32 = __esm({
|
|
1224
|
-
"../../packages/shared/tw/hashing/crc32.ts"() {
|
|
1225
|
-
crc32Table = (() => {
|
|
1226
|
-
const table = new Uint32Array(256);
|
|
1227
|
-
for (let i = 0; i < 256; i++) {
|
|
1228
|
-
let crc = i;
|
|
1229
|
-
for (let j = 0; j < 8; j++) {
|
|
1230
|
-
crc = crc & 1 ? 3988292384 ^ crc >>> 1 : crc >>> 1;
|
|
1231
|
-
}
|
|
1232
|
-
table[i] = crc >>> 0;
|
|
1233
|
-
}
|
|
1234
|
-
return table;
|
|
1235
|
-
})();
|
|
1236
|
-
}
|
|
1237
|
-
});
|
|
1238
|
-
|
|
1239
|
-
// ../../packages/shared/tw/hashing/sha.ts
|
|
1240
|
-
function syncHashFallback(algorithm, data) {
|
|
1241
|
-
const input = typeof data === "string" ? data : new TextDecoder().decode(data);
|
|
1242
|
-
let hash2 = 2166136261;
|
|
1243
|
-
for (let i = 0; i < input.length; i++) {
|
|
1244
|
-
hash2 ^= input.charCodeAt(i);
|
|
1245
|
-
hash2 = Math.imul(hash2, 16777619);
|
|
1246
|
-
}
|
|
1247
|
-
return (hash2 >>> 0).toString(16).padStart(8, "0").repeat(8);
|
|
1248
|
-
}
|
|
1249
|
-
function sha256(data) {
|
|
1250
|
-
try {
|
|
1251
|
-
const hasher = new BunHashSync("sha256");
|
|
1252
|
-
return hasher.update(data).digest("hex");
|
|
1253
|
-
} catch {
|
|
1254
|
-
return syncHashFallback("sha256", data);
|
|
1083
|
+
function parseCacheProfilesSource(src) {
|
|
1084
|
+
const out = {};
|
|
1085
|
+
const cleaned = src.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*/gm, "");
|
|
1086
|
+
const cacheKey = /\bcache\s*:\s*\{/.exec(cleaned);
|
|
1087
|
+
if (!cacheKey) return out;
|
|
1088
|
+
const cacheOpen = cleaned.indexOf("{", cacheKey.index);
|
|
1089
|
+
const cacheClose = balancedEnd(cleaned, cacheOpen);
|
|
1090
|
+
if (cacheClose < 0) return out;
|
|
1091
|
+
const cacheBody = cleaned.slice(cacheOpen + 1, cacheClose);
|
|
1092
|
+
const profKey = /\bprofiles\s*:\s*\{/.exec(cacheBody);
|
|
1093
|
+
if (!profKey) return out;
|
|
1094
|
+
const profOpen = cacheBody.indexOf("{", profKey.index);
|
|
1095
|
+
const profClose = balancedEnd(cacheBody, profOpen);
|
|
1096
|
+
if (profClose < 0) return out;
|
|
1097
|
+
const profBody = cacheBody.slice(profOpen + 1, profClose);
|
|
1098
|
+
for (const m of profBody.matchAll(/([A-Za-z0-9_]+)\s*:\s*\{([^}]*)\}/g)) {
|
|
1099
|
+
const profile = {};
|
|
1100
|
+
for (const kv of m[2].matchAll(/(stale|revalidate|expire)\s*:\s*(\d+)/g)) {
|
|
1101
|
+
profile[kv[1]] = Number(kv[2]);
|
|
1102
|
+
}
|
|
1103
|
+
if (Object.keys(profile).length > 0) out[m[1]] = profile;
|
|
1255
1104
|
}
|
|
1105
|
+
return out;
|
|
1256
1106
|
}
|
|
1257
|
-
var
|
|
1258
|
-
var
|
|
1259
|
-
"../../packages/shared/tw/
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
if (nodeCrypto?.createHash) {
|
|
1267
|
-
this.hasher = nodeCrypto.createHash(algorithm);
|
|
1268
|
-
} else {
|
|
1269
|
-
throw new Error("No sync hash available");
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
update(data) {
|
|
1273
|
-
const input = typeof data === "string" ? data : Buffer.from(data);
|
|
1274
|
-
this.hasher.update(input);
|
|
1275
|
-
return this;
|
|
1276
|
-
}
|
|
1277
|
-
digest(encoding) {
|
|
1278
|
-
return this.hasher.digest(encoding);
|
|
1279
|
-
}
|
|
1280
|
-
};
|
|
1281
|
-
}
|
|
1282
|
-
});
|
|
1283
|
-
|
|
1284
|
-
// ../../packages/shared/tw/utils.ts
|
|
1285
|
-
var init_utils = __esm({
|
|
1286
|
-
"../../packages/shared/tw/utils.ts"() {
|
|
1287
|
-
}
|
|
1288
|
-
});
|
|
1289
|
-
|
|
1290
|
-
// ../../packages/shared/tw/fs/runtime-compat.ts
|
|
1291
|
-
var init_runtime_compat = __esm({
|
|
1292
|
-
"../../packages/shared/tw/fs/runtime-compat.ts"() {
|
|
1293
|
-
}
|
|
1294
|
-
});
|
|
1295
|
-
|
|
1296
|
-
// ../../packages/shared/tw/constants/misc.ts
|
|
1297
|
-
var MIME_TYPES, VLQ_BASE_SHIFT, VLQ_BASE, VLQ_BASE_MASK;
|
|
1298
|
-
var init_misc = __esm({
|
|
1299
|
-
"../../packages/shared/tw/constants/misc.ts"() {
|
|
1300
|
-
MIME_TYPES = {
|
|
1301
|
-
".html": "text/html",
|
|
1302
|
-
".htm": "text/html",
|
|
1303
|
-
".css": "text/css",
|
|
1304
|
-
".js": "text/javascript",
|
|
1305
|
-
".mjs": "text/javascript",
|
|
1306
|
-
".json": "application/json",
|
|
1307
|
-
".xml": "application/xml",
|
|
1308
|
-
".txt": "text/plain",
|
|
1309
|
-
".md": "text/markdown",
|
|
1310
|
-
".csv": "text/csv",
|
|
1311
|
-
".svg": "image/svg+xml",
|
|
1312
|
-
".png": "image/png",
|
|
1313
|
-
".jpg": "image/jpeg",
|
|
1314
|
-
".jpeg": "image/jpeg",
|
|
1315
|
-
".gif": "image/gif",
|
|
1316
|
-
".webp": "image/webp",
|
|
1317
|
-
".avif": "image/avif",
|
|
1318
|
-
".ico": "image/x-icon",
|
|
1319
|
-
".bmp": "image/bmp",
|
|
1320
|
-
".woff": "font/woff",
|
|
1321
|
-
".woff2": "font/woff2",
|
|
1322
|
-
".ttf": "font/ttf",
|
|
1323
|
-
".otf": "font/otf",
|
|
1324
|
-
".pdf": "application/pdf",
|
|
1325
|
-
".zip": "application/zip",
|
|
1326
|
-
".gz": "application/gzip",
|
|
1327
|
-
".tar": "application/x-tar",
|
|
1328
|
-
".mp4": "video/mp4",
|
|
1329
|
-
".webm": "video/webm",
|
|
1330
|
-
".mp3": "audio/mpeg",
|
|
1331
|
-
".ogg": "audio/ogg",
|
|
1332
|
-
".wav": "audio/wav",
|
|
1333
|
-
".wasm": "application/wasm",
|
|
1334
|
-
".tw": "text/tw",
|
|
1335
|
-
".tss": "text/tss",
|
|
1336
|
-
".toml": "application/toml",
|
|
1337
|
-
".yaml": "application/yaml",
|
|
1338
|
-
".yml": "application/yaml",
|
|
1339
|
-
".webmanifest": "application/manifest+json"
|
|
1340
|
-
};
|
|
1341
|
-
VLQ_BASE_SHIFT = 5;
|
|
1342
|
-
VLQ_BASE = 1 << VLQ_BASE_SHIFT;
|
|
1343
|
-
VLQ_BASE_MASK = VLQ_BASE - 1;
|
|
1344
|
-
}
|
|
1345
|
-
});
|
|
1346
|
-
|
|
1347
|
-
// ../../packages/shared/tw/fs/async.ts
|
|
1348
|
-
var init_async = __esm({
|
|
1349
|
-
"../../packages/shared/tw/fs/async.ts"() {
|
|
1350
|
-
init_runtime_compat();
|
|
1351
|
-
init_runtime_compat();
|
|
1352
|
-
init_misc();
|
|
1353
|
-
}
|
|
1354
|
-
});
|
|
1355
|
-
|
|
1356
|
-
// ../../packages/runtime/tw/utils/path-utils.ts
|
|
1357
|
-
var init_path_utils = __esm({
|
|
1358
|
-
"../../packages/runtime/tw/utils/path-utils.ts"() {
|
|
1359
|
-
}
|
|
1360
|
-
});
|
|
1361
|
-
|
|
1362
|
-
// ../../packages/shared/tw/fs/glob.ts
|
|
1363
|
-
var init_glob = __esm({
|
|
1364
|
-
"../../packages/shared/tw/fs/glob.ts"() {
|
|
1365
|
-
init_runtime_compat();
|
|
1366
|
-
init_path_utils();
|
|
1367
|
-
}
|
|
1368
|
-
});
|
|
1369
|
-
|
|
1370
|
-
// ../../packages/shared/tw/fs/sync.ts
|
|
1371
|
-
var init_sync = __esm({
|
|
1372
|
-
"../../packages/shared/tw/fs/sync.ts"() {
|
|
1373
|
-
init_runtime_compat();
|
|
1374
|
-
init_runtime_compat();
|
|
1375
|
-
init_async();
|
|
1376
|
-
}
|
|
1377
|
-
});
|
|
1378
|
-
|
|
1379
|
-
// ../../packages/shared/tw/fs/watch.ts
|
|
1380
|
-
var init_watch = __esm({
|
|
1381
|
-
"../../packages/shared/tw/fs/watch.ts"() {
|
|
1382
|
-
init_sync();
|
|
1383
|
-
init_runtime_compat();
|
|
1384
|
-
}
|
|
1385
|
-
});
|
|
1386
|
-
|
|
1387
|
-
// ../../packages/shared/tw/fs/index.ts
|
|
1388
|
-
var init_fs = __esm({
|
|
1389
|
-
"../../packages/shared/tw/fs/index.ts"() {
|
|
1390
|
-
init_async();
|
|
1391
|
-
init_glob();
|
|
1392
|
-
init_sync();
|
|
1393
|
-
init_watch();
|
|
1394
|
-
}
|
|
1395
|
-
});
|
|
1396
|
-
|
|
1397
|
-
// ../../packages/shared/tw/config/defaults.ts
|
|
1398
|
-
var init_defaults = __esm({
|
|
1399
|
-
"../../packages/shared/tw/config/defaults.ts"() {
|
|
1400
|
-
init_fs();
|
|
1401
|
-
}
|
|
1402
|
-
});
|
|
1403
|
-
|
|
1404
|
-
// ../../packages/shared/tw/logger/levels.ts
|
|
1405
|
-
var init_levels = __esm({
|
|
1406
|
-
"../../packages/shared/tw/logger/levels.ts"() {
|
|
1407
|
-
}
|
|
1408
|
-
});
|
|
1409
|
-
|
|
1410
|
-
// ../../packages/shared/tw/logger/colors.ts
|
|
1411
|
-
var ANSI, LEVEL_COLORS, LEVEL_ICONS;
|
|
1412
|
-
var init_colors = __esm({
|
|
1413
|
-
"../../packages/shared/tw/logger/colors.ts"() {
|
|
1414
|
-
init_levels();
|
|
1415
|
-
ANSI = {
|
|
1416
|
-
reset: "\x1B[0m",
|
|
1417
|
-
bold: "\x1B[1m",
|
|
1418
|
-
dim: "\x1B[2m",
|
|
1419
|
-
italic: "\x1B[3m",
|
|
1420
|
-
underline: "\x1B[4m",
|
|
1421
|
-
blink: "\x1B[5m",
|
|
1422
|
-
inverse: "\x1B[7m",
|
|
1423
|
-
hidden: "\x1B[8m",
|
|
1424
|
-
strikethrough: "\x1B[9m",
|
|
1425
|
-
black: "\x1B[30m",
|
|
1426
|
-
red: "\x1B[31m",
|
|
1427
|
-
green: "\x1B[32m",
|
|
1428
|
-
yellow: "\x1B[33m",
|
|
1429
|
-
blue: "\x1B[34m",
|
|
1430
|
-
magenta: "\x1B[35m",
|
|
1431
|
-
cyan: "\x1B[36m",
|
|
1432
|
-
white: "\x1B[37m",
|
|
1433
|
-
bgBlack: "\x1B[40m",
|
|
1434
|
-
bgRed: "\x1B[41m",
|
|
1435
|
-
bgGreen: "\x1B[42m",
|
|
1436
|
-
bgYellow: "\x1B[44m",
|
|
1437
|
-
bgBlue: "\x1B[44m",
|
|
1438
|
-
bgMagenta: "\x1B[45m",
|
|
1439
|
-
bgCyan: "\x1B[46m",
|
|
1440
|
-
bgWhite: "\x1B[47m",
|
|
1441
|
-
brightBlack: "\x1B[90m",
|
|
1442
|
-
brightRed: "\x1B[91m",
|
|
1443
|
-
brightGreen: "\x1B[92m",
|
|
1444
|
-
brightYellow: "\x1B[93m",
|
|
1445
|
-
brightBlue: "\x1B[94m",
|
|
1446
|
-
brightMagenta: "\x1B[95m",
|
|
1447
|
-
brightCyan: "\x1B[96m",
|
|
1448
|
-
brightWhite: "\x1B[97m"
|
|
1449
|
-
};
|
|
1450
|
-
LEVEL_COLORS = {
|
|
1451
|
-
[0 /* SILENT */]: ANSI.dim,
|
|
1452
|
-
[1 /* ERROR */]: ANSI.red,
|
|
1453
|
-
[2 /* WARN */]: ANSI.yellow,
|
|
1454
|
-
[3 /* INFO */]: ANSI.blue,
|
|
1455
|
-
[4 /* DEBUG */]: ANSI.magenta,
|
|
1456
|
-
[5 /* TRACE */]: ANSI.dim
|
|
1457
|
-
};
|
|
1458
|
-
LEVEL_ICONS = {
|
|
1459
|
-
[0 /* SILENT */]: "",
|
|
1460
|
-
[1 /* ERROR */]: "?",
|
|
1461
|
-
[2 /* WARN */]: "?",
|
|
1462
|
-
[3 /* INFO */]: "?",
|
|
1463
|
-
[4 /* DEBUG */]: "o",
|
|
1464
|
-
[5 /* TRACE */]: "o"
|
|
1107
|
+
var BUILTIN_CACHE_PROFILES;
|
|
1108
|
+
var init_cache = __esm({
|
|
1109
|
+
"../../packages/shared/tw/cache.ts"() {
|
|
1110
|
+
BUILTIN_CACHE_PROFILES = {
|
|
1111
|
+
seconds: { stale: 1, revalidate: 1, expire: 2 },
|
|
1112
|
+
minutes: { stale: 60, revalidate: 300, expire: 3600 },
|
|
1113
|
+
hours: { stale: 3600, revalidate: 14400, expire: 86400 },
|
|
1114
|
+
days: { stale: 86400, revalidate: 604800, expire: 2592e3 },
|
|
1115
|
+
max: { stale: 2592e3, revalidate: 5184e3, expire: 31536e3 }
|
|
1465
1116
|
};
|
|
1466
1117
|
}
|
|
1467
1118
|
});
|
|
1468
1119
|
|
|
1469
|
-
// ../../packages/shared/tw/logger/transports.ts
|
|
1470
|
-
var init_transports = __esm({
|
|
1471
|
-
"../../packages/shared/tw/logger/transports.ts"() {
|
|
1472
|
-
init_colors();
|
|
1473
|
-
}
|
|
1474
|
-
});
|
|
1475
|
-
|
|
1476
|
-
// ../../packages/shared/tw/logger/logger.ts
|
|
1477
|
-
var init_logger = __esm({
|
|
1478
|
-
"../../packages/shared/tw/logger/logger.ts"() {
|
|
1479
|
-
init_levels();
|
|
1480
|
-
init_transports();
|
|
1481
|
-
}
|
|
1482
|
-
});
|
|
1483
|
-
|
|
1484
|
-
// ../../packages/shared/tw/logger/progress.ts
|
|
1485
|
-
var init_progress = __esm({
|
|
1486
|
-
"../../packages/shared/tw/logger/progress.ts"() {
|
|
1487
|
-
init_logger();
|
|
1488
|
-
init_colors();
|
|
1489
|
-
}
|
|
1490
|
-
});
|
|
1491
|
-
|
|
1492
|
-
// ../../packages/shared/tw/logger/index.ts
|
|
1493
|
-
var init_logger2 = __esm({
|
|
1494
|
-
"../../packages/shared/tw/logger/index.ts"() {
|
|
1495
|
-
init_colors();
|
|
1496
|
-
init_levels();
|
|
1497
|
-
init_logger();
|
|
1498
|
-
init_progress();
|
|
1499
|
-
init_transports();
|
|
1500
|
-
}
|
|
1501
|
-
});
|
|
1502
|
-
|
|
1503
|
-
// ../../packages/shared/tw/config/validate.ts
|
|
1504
|
-
var init_validate = __esm({
|
|
1505
|
-
"../../packages/shared/tw/config/validate.ts"() {
|
|
1506
|
-
init_logger2();
|
|
1507
|
-
}
|
|
1508
|
-
});
|
|
1509
|
-
|
|
1510
|
-
// ../../packages/shared/tw/config/loader.ts
|
|
1511
|
-
var init_loader = __esm({
|
|
1512
|
-
"../../packages/shared/tw/config/loader.ts"() {
|
|
1513
|
-
init_defaults();
|
|
1514
|
-
init_fs();
|
|
1515
|
-
init_logger2();
|
|
1516
|
-
init_validate();
|
|
1517
|
-
}
|
|
1518
|
-
});
|
|
1519
|
-
|
|
1520
|
-
// ../../packages/shared/tw/config/index.ts
|
|
1521
|
-
var init_config = __esm({
|
|
1522
|
-
"../../packages/shared/tw/config/index.ts"() {
|
|
1523
|
-
init_defaults();
|
|
1524
|
-
init_loader();
|
|
1525
|
-
init_validate();
|
|
1526
|
-
}
|
|
1527
|
-
});
|
|
1528
|
-
|
|
1529
1120
|
// ../../packages/shared/tw/constants/aria.ts
|
|
1530
1121
|
var init_aria = __esm({
|
|
1531
1122
|
"../../packages/shared/tw/constants/aria.ts"() {
|
|
@@ -1877,195 +1468,781 @@ var init_html = __esm({
|
|
|
1877
1468
|
}
|
|
1878
1469
|
});
|
|
1879
1470
|
|
|
1880
|
-
// ../../packages/shared/tw/constants/http.ts
|
|
1881
|
-
var init_http = __esm({
|
|
1882
|
-
"../../packages/shared/tw/constants/http.ts"() {
|
|
1471
|
+
// ../../packages/shared/tw/constants/http.ts
|
|
1472
|
+
var init_http = __esm({
|
|
1473
|
+
"../../packages/shared/tw/constants/http.ts"() {
|
|
1474
|
+
}
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
// ../../packages/shared/tw/constants/index.ts
|
|
1478
|
+
var init_constants = __esm({
|
|
1479
|
+
"../../packages/shared/tw/constants/index.ts"() {
|
|
1480
|
+
init_aria();
|
|
1481
|
+
init_css();
|
|
1482
|
+
init_events();
|
|
1483
|
+
init_html();
|
|
1484
|
+
init_http();
|
|
1485
|
+
init_misc();
|
|
1486
|
+
}
|
|
1487
|
+
});
|
|
1488
|
+
|
|
1489
|
+
// ../../packages/shared/tw/crypto/index.ts
|
|
1490
|
+
var init_crypto = __esm({
|
|
1491
|
+
"../../packages/shared/tw/crypto/index.ts"() {
|
|
1492
|
+
}
|
|
1493
|
+
});
|
|
1494
|
+
|
|
1495
|
+
// ../../packages/shared/tw/env/environment.ts
|
|
1496
|
+
var init_environment = __esm({
|
|
1497
|
+
"../../packages/shared/tw/env/environment.ts"() {
|
|
1498
|
+
}
|
|
1499
|
+
});
|
|
1500
|
+
|
|
1501
|
+
// ../../packages/shared/tw/env/index.ts
|
|
1502
|
+
var init_env = __esm({
|
|
1503
|
+
"../../packages/shared/tw/env/index.ts"() {
|
|
1504
|
+
init_environment();
|
|
1505
|
+
}
|
|
1506
|
+
});
|
|
1507
|
+
|
|
1508
|
+
// ../../packages/shared/tw/hashing/etag.ts
|
|
1509
|
+
var init_etag = __esm({
|
|
1510
|
+
"../../packages/shared/tw/hashing/etag.ts"() {
|
|
1511
|
+
init_md5();
|
|
1512
|
+
init_sha();
|
|
1513
|
+
}
|
|
1514
|
+
});
|
|
1515
|
+
|
|
1516
|
+
// ../../packages/shared/tw/hashing/hmac.ts
|
|
1517
|
+
var init_hmac = __esm({
|
|
1518
|
+
"../../packages/shared/tw/hashing/hmac.ts"() {
|
|
1519
|
+
}
|
|
1520
|
+
});
|
|
1521
|
+
|
|
1522
|
+
// ../../packages/shared/tw/hashing/merkle.ts
|
|
1523
|
+
var init_merkle = __esm({
|
|
1524
|
+
"../../packages/shared/tw/hashing/merkle.ts"() {
|
|
1525
|
+
init_sha();
|
|
1526
|
+
}
|
|
1527
|
+
});
|
|
1528
|
+
|
|
1529
|
+
// ../../packages/shared/tw/hashing/uuid.ts
|
|
1530
|
+
var init_uuid = __esm({
|
|
1531
|
+
"../../packages/shared/tw/hashing/uuid.ts"() {
|
|
1532
|
+
}
|
|
1533
|
+
});
|
|
1534
|
+
|
|
1535
|
+
// ../../packages/shared/tw/hashing/index.ts
|
|
1536
|
+
var init_hashing = __esm({
|
|
1537
|
+
"../../packages/shared/tw/hashing/index.ts"() {
|
|
1538
|
+
init_crc32();
|
|
1539
|
+
init_etag();
|
|
1540
|
+
init_hmac();
|
|
1541
|
+
init_md5();
|
|
1542
|
+
init_merkle();
|
|
1543
|
+
init_sha();
|
|
1544
|
+
init_uuid();
|
|
1545
|
+
}
|
|
1546
|
+
});
|
|
1547
|
+
|
|
1548
|
+
// ../../packages/shared/tw/routing/constants.ts
|
|
1549
|
+
function getRouteFileType(baseName) {
|
|
1550
|
+
return SPECIAL_FILES[baseName] ?? null;
|
|
1551
|
+
}
|
|
1552
|
+
function isPrivateFolder(name) {
|
|
1553
|
+
return name.startsWith("_");
|
|
1554
|
+
}
|
|
1555
|
+
function extractGroupName(name) {
|
|
1556
|
+
const m = name.match(/^\(([^)]+)\)$/);
|
|
1557
|
+
if (!m) return null;
|
|
1558
|
+
if (m[1].startsWith(".")) return null;
|
|
1559
|
+
return m[1];
|
|
1560
|
+
}
|
|
1561
|
+
function parseInterceptingFolder(name) {
|
|
1562
|
+
const m = name.match(/^\((\.{1,3})([^)]+)\)$/);
|
|
1563
|
+
if (!m) return null;
|
|
1564
|
+
return { levels: m[1].length, target: m[2] };
|
|
1565
|
+
}
|
|
1566
|
+
function parseSegment(name) {
|
|
1567
|
+
const optCatchAll = name.match(/^\[\[\.\.\.([^\]]+)\]\]$/);
|
|
1568
|
+
if (optCatchAll) return { type: "optional-catch-all", name: optCatchAll[1] };
|
|
1569
|
+
const catchAll = name.match(/^\[\.\.\.([^\]]+)\]$/);
|
|
1570
|
+
if (catchAll) return { type: "catch-all", name: catchAll[1] };
|
|
1571
|
+
const dynamic = name.match(/^\[([^\]]+)\]$/);
|
|
1572
|
+
if (dynamic) return { type: "dynamic", name: dynamic[1] };
|
|
1573
|
+
return { type: "static", name };
|
|
1574
|
+
}
|
|
1575
|
+
function extractSlotName(name) {
|
|
1576
|
+
if (!name.startsWith("@")) return null;
|
|
1577
|
+
return name.slice(1);
|
|
1578
|
+
}
|
|
1579
|
+
var TW_EXTENSION, TWM_EXTENSION, ROUTE_EXTENSIONS, SPECIAL_FILES;
|
|
1580
|
+
var init_constants2 = __esm({
|
|
1581
|
+
"../../packages/shared/tw/routing/constants.ts"() {
|
|
1582
|
+
TW_EXTENSION = ".tw";
|
|
1583
|
+
TWM_EXTENSION = ".twm";
|
|
1584
|
+
ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([TW_EXTENSION, TWM_EXTENSION]);
|
|
1585
|
+
SPECIAL_FILES = {
|
|
1586
|
+
"page": "page",
|
|
1587
|
+
"index": "page",
|
|
1588
|
+
// Intercepting routes (docs/intercepting-routes.md): a `(.)page.tw`
|
|
1589
|
+
// beside a page renders INSTEAD of it on SPA navigation (X-TW-Navigate).
|
|
1590
|
+
"(.)page": "intercept-page",
|
|
1591
|
+
"layout": "layout",
|
|
1592
|
+
"template": "template",
|
|
1593
|
+
"loading": "loading",
|
|
1594
|
+
"error": "error",
|
|
1595
|
+
"not-found": "not-found",
|
|
1596
|
+
"global-error": "global-error",
|
|
1597
|
+
"default": "default",
|
|
1598
|
+
"head": "head",
|
|
1599
|
+
"route": "route",
|
|
1600
|
+
"middleware": "middleware"
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
});
|
|
1604
|
+
|
|
1605
|
+
// ../../packages/shared/tw/routing/index.ts
|
|
1606
|
+
var init_routing = __esm({
|
|
1607
|
+
"../../packages/shared/tw/routing/index.ts"() {
|
|
1608
|
+
init_constants2();
|
|
1883
1609
|
}
|
|
1884
1610
|
});
|
|
1885
1611
|
|
|
1886
|
-
// ../../packages/shared/tw/
|
|
1887
|
-
var
|
|
1888
|
-
"../../packages/shared/tw/
|
|
1889
|
-
init_aria();
|
|
1890
|
-
init_css();
|
|
1891
|
-
init_events();
|
|
1892
|
-
init_html();
|
|
1893
|
-
init_http();
|
|
1894
|
-
init_misc();
|
|
1612
|
+
// ../../packages/shared/tw/types/common.ts
|
|
1613
|
+
var init_common = __esm({
|
|
1614
|
+
"../../packages/shared/tw/types/common.ts"() {
|
|
1895
1615
|
}
|
|
1896
1616
|
});
|
|
1897
1617
|
|
|
1898
|
-
// ../../packages/shared/tw/
|
|
1899
|
-
var
|
|
1900
|
-
"../../packages/shared/tw/
|
|
1618
|
+
// ../../packages/shared/tw/types/errors.ts
|
|
1619
|
+
var init_errors = __esm({
|
|
1620
|
+
"../../packages/shared/tw/types/errors.ts"() {
|
|
1901
1621
|
}
|
|
1902
1622
|
});
|
|
1903
1623
|
|
|
1904
|
-
// ../../packages/shared/tw/
|
|
1905
|
-
var
|
|
1906
|
-
"../../packages/shared/tw/
|
|
1624
|
+
// ../../packages/shared/tw/types/results.ts
|
|
1625
|
+
var init_results = __esm({
|
|
1626
|
+
"../../packages/shared/tw/types/results.ts"() {
|
|
1907
1627
|
}
|
|
1908
1628
|
});
|
|
1909
1629
|
|
|
1910
|
-
// ../../packages/shared/tw/
|
|
1911
|
-
var
|
|
1912
|
-
"../../packages/shared/tw/
|
|
1913
|
-
|
|
1630
|
+
// ../../packages/shared/tw/types/index.ts
|
|
1631
|
+
var init_types = __esm({
|
|
1632
|
+
"../../packages/shared/tw/types/index.ts"() {
|
|
1633
|
+
init_common();
|
|
1634
|
+
init_errors();
|
|
1635
|
+
init_results();
|
|
1914
1636
|
}
|
|
1915
1637
|
});
|
|
1916
1638
|
|
|
1917
|
-
// ../../packages/shared/tw/
|
|
1918
|
-
var
|
|
1919
|
-
"../../packages/shared/tw/
|
|
1920
|
-
|
|
1639
|
+
// ../../packages/shared/tw/utils/string/transform.ts
|
|
1640
|
+
var init_transform = __esm({
|
|
1641
|
+
"../../packages/shared/tw/utils/string/transform.ts"() {
|
|
1642
|
+
}
|
|
1643
|
+
});
|
|
1644
|
+
|
|
1645
|
+
// ../../packages/shared/tw/index.ts
|
|
1646
|
+
var init_tw = __esm({
|
|
1647
|
+
"../../packages/shared/tw/index.ts"() {
|
|
1921
1648
|
init_sha();
|
|
1649
|
+
init_utils();
|
|
1650
|
+
init_config();
|
|
1651
|
+
init_cache();
|
|
1652
|
+
init_constants();
|
|
1653
|
+
init_crypto();
|
|
1654
|
+
init_env();
|
|
1655
|
+
init_fs();
|
|
1656
|
+
init_hashing();
|
|
1657
|
+
init_logger2();
|
|
1658
|
+
init_routing();
|
|
1659
|
+
init_types();
|
|
1660
|
+
init_transform();
|
|
1922
1661
|
}
|
|
1923
1662
|
});
|
|
1924
1663
|
|
|
1925
|
-
// ../../packages/shared/tw/
|
|
1926
|
-
var
|
|
1927
|
-
|
|
1664
|
+
// ../../packages/shared/tw/node-import.ts
|
|
1665
|
+
var node_import_exports = {};
|
|
1666
|
+
__export(node_import_exports, {
|
|
1667
|
+
twImportTs: () => twImportTs
|
|
1668
|
+
});
|
|
1669
|
+
async function twImportTs(tsPath) {
|
|
1670
|
+
if (!globalThis.__TW_NODE) {
|
|
1671
|
+
return import(tsPath);
|
|
1672
|
+
}
|
|
1673
|
+
const esbuildMain = globalThis.__TW_ESBUILD_MODULE;
|
|
1674
|
+
const esbuild = await (esbuildMain ? import(esbuildMain) : import("esbuild"));
|
|
1675
|
+
const safe = tsPath.replace(/[^a-zA-Z0-9]/g, "_").slice(-120);
|
|
1676
|
+
const cachePath = (0, import_node_path.join)((0, import_node_os.tmpdir)(), "tw-" + safe + ".mjs");
|
|
1677
|
+
await esbuild.build({
|
|
1678
|
+
entryPoints: [tsPath],
|
|
1679
|
+
bundle: true,
|
|
1680
|
+
format: "esm",
|
|
1681
|
+
platform: "node",
|
|
1682
|
+
target: "node18",
|
|
1683
|
+
outfile: cachePath,
|
|
1684
|
+
logLevel: "silent"
|
|
1685
|
+
});
|
|
1686
|
+
return import((0, import_node_url.pathToFileURL)(cachePath).href);
|
|
1687
|
+
}
|
|
1688
|
+
var import_node_path, import_node_os, import_node_url;
|
|
1689
|
+
var init_node_import = __esm({
|
|
1690
|
+
"../../packages/shared/tw/node-import.ts"() {
|
|
1691
|
+
import_node_path = require("node:path");
|
|
1692
|
+
import_node_os = require("node:os");
|
|
1693
|
+
import_node_url = require("node:url");
|
|
1694
|
+
}
|
|
1695
|
+
});
|
|
1696
|
+
|
|
1697
|
+
// ../../packages/image/tw/index.ts
|
|
1698
|
+
function optimizedImageUrl(src, opts = {}) {
|
|
1699
|
+
const p = new URLSearchParams();
|
|
1700
|
+
if (opts.width) p.set("w", String(opts.width));
|
|
1701
|
+
if (opts.height) p.set("h", String(opts.height));
|
|
1702
|
+
if (opts.quality) p.set("q", String(opts.quality));
|
|
1703
|
+
p.set("f", opts.format ?? "auto");
|
|
1704
|
+
const qs = p.toString();
|
|
1705
|
+
return `/_tw/img${src.startsWith("/") ? src : "/" + src}?${qs}`;
|
|
1706
|
+
}
|
|
1707
|
+
function imageAttributesFor(opts, config = DEFAULT_IMAGE_CONFIG) {
|
|
1708
|
+
const width = opts.width ?? 0;
|
|
1709
|
+
const quality = opts.quality ?? config.quality;
|
|
1710
|
+
const format = config.formats[0] ?? "webp";
|
|
1711
|
+
const isRemote = /^https?:\/\//i.test(opts.src);
|
|
1712
|
+
const src = isRemote ? `/_tw/img/_remote?src=${encodeURIComponent(opts.src)}&w=${opts.width ?? ""}&h=${opts.height ?? ""}&q=${quality}&f=auto` : optimizedImageUrl(opts.src, { width: opts.width, height: opts.height, quality });
|
|
1713
|
+
const widths = config.breakpoints.filter((bp) => width === 0 || bp <= width * 2);
|
|
1714
|
+
const srcset = widths.map(
|
|
1715
|
+
(bp) => isRemote ? `/_tw/img/_remote?src=${encodeURIComponent(opts.src)}&w=${bp}&q=${quality}&f=auto ${bp}w` : `${optimizedImageUrl(opts.src, { width: bp, quality })} ${bp}w`
|
|
1716
|
+
).join(", ");
|
|
1717
|
+
const sizes = width > 0 ? `(max-width: ${width}px) 100vw, ${width}px` : "100vw";
|
|
1718
|
+
const loading = opts.priority ? "eager" : "lazy";
|
|
1719
|
+
return { src, srcset, sizes, width, height: opts.height ?? 0, loading, decoding: "async", format };
|
|
1720
|
+
}
|
|
1721
|
+
function createImageHandler(options) {
|
|
1722
|
+
const rootDir = (0, import_node_path2.resolve)(options.rootDir);
|
|
1723
|
+
const sourceDir = (0, import_node_path2.resolve)(options.sourceDir ?? (0, import_node_path2.join)(rootDir, "public"));
|
|
1724
|
+
const cacheDir = (0, import_node_path2.resolve)(options.cacheDir ?? (0, import_node_path2.join)(rootDir, ".tw", "img", "cache"));
|
|
1725
|
+
const config = { ...DEFAULT_IMAGE_CONFIG, ...options.config };
|
|
1726
|
+
let sharpPromise = null;
|
|
1727
|
+
let warnedNoSharp = false;
|
|
1728
|
+
async function loadSharp() {
|
|
1729
|
+
if (!sharpPromise) {
|
|
1730
|
+
sharpPromise = (async () => {
|
|
1731
|
+
let req;
|
|
1732
|
+
try {
|
|
1733
|
+
const { createRequire } = await import("node:module");
|
|
1734
|
+
req = createRequire((0, import_node_path2.join)(rootDir, "package.json"));
|
|
1735
|
+
} catch {
|
|
1736
|
+
}
|
|
1737
|
+
const candidates = [
|
|
1738
|
+
(0, import_node_path2.join)(rootDir, "node_modules", "sharp"),
|
|
1739
|
+
"sharp"
|
|
1740
|
+
];
|
|
1741
|
+
for (const candidate of candidates) {
|
|
1742
|
+
if (typeof req === "function") {
|
|
1743
|
+
try {
|
|
1744
|
+
const m = req(candidate);
|
|
1745
|
+
const mod = m?.default ?? m;
|
|
1746
|
+
if (mod && typeof mod === "function") return mod;
|
|
1747
|
+
} catch {
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
try {
|
|
1751
|
+
const href = candidate.startsWith("/") ? (0, import_node_url2.pathToFileURL)(candidate).href : candidate;
|
|
1752
|
+
const m = await import(href);
|
|
1753
|
+
const mod = m?.default ?? m;
|
|
1754
|
+
if (mod && typeof mod === "function") return mod;
|
|
1755
|
+
} catch {
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
return null;
|
|
1759
|
+
})();
|
|
1760
|
+
}
|
|
1761
|
+
return sharpPromise;
|
|
1762
|
+
}
|
|
1763
|
+
function contentTypeFor(path) {
|
|
1764
|
+
return IMAGE_EXTENSIONS[(0, import_node_path2.extname)(path).toLowerCase()] ?? "application/octet-stream";
|
|
1765
|
+
}
|
|
1766
|
+
function negotiateFormat(requested, accept) {
|
|
1767
|
+
if (requested && requested !== "auto") {
|
|
1768
|
+
return SHARP_FORMATS[requested.toLowerCase()] ?? null;
|
|
1769
|
+
}
|
|
1770
|
+
const acceptLc = (accept ?? "").toLowerCase();
|
|
1771
|
+
for (const f of config.formats) {
|
|
1772
|
+
if (acceptLc.includes(`image/${f}`)) return f === "jpeg" ? "jpeg" : f;
|
|
1773
|
+
}
|
|
1774
|
+
return null;
|
|
1775
|
+
}
|
|
1776
|
+
function isRemoteSource(src) {
|
|
1777
|
+
return /^https?:\/\//i.test(src);
|
|
1778
|
+
}
|
|
1779
|
+
function remoteAllowed(url) {
|
|
1780
|
+
if (!config.remoteAllowHosts.includes(url.hostname)) return false;
|
|
1781
|
+
if (url.username || url.password) return false;
|
|
1782
|
+
return url.protocol === "https:" || url.protocol === "http:";
|
|
1783
|
+
}
|
|
1784
|
+
function isPrivateAddress(host) {
|
|
1785
|
+
const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
|
|
1786
|
+
if (m) {
|
|
1787
|
+
const a = Number(m[1]), b = Number(m[2]);
|
|
1788
|
+
if (a === 0 || a === 10 || a === 127) return true;
|
|
1789
|
+
if (a === 169 && b === 254) return true;
|
|
1790
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
1791
|
+
if (a === 192 && b === 168) return true;
|
|
1792
|
+
if (a >= 224) return true;
|
|
1793
|
+
return false;
|
|
1794
|
+
}
|
|
1795
|
+
const h = host.toLowerCase();
|
|
1796
|
+
if (h === "::" || h === "::1") return true;
|
|
1797
|
+
if (h.startsWith("fe80:")) return true;
|
|
1798
|
+
if (h.startsWith("fc") || h.startsWith("fd")) return true;
|
|
1799
|
+
if (h.startsWith("::ffff:")) return isPrivateAddress(h.slice(7));
|
|
1800
|
+
return false;
|
|
1801
|
+
}
|
|
1802
|
+
async function safeFetchRemote(src) {
|
|
1803
|
+
let url;
|
|
1804
|
+
try {
|
|
1805
|
+
url = new URL(src);
|
|
1806
|
+
} catch {
|
|
1807
|
+
return null;
|
|
1808
|
+
}
|
|
1809
|
+
for (let hop = 0; hop < 3; hop++) {
|
|
1810
|
+
if (!remoteAllowed(url)) return null;
|
|
1811
|
+
try {
|
|
1812
|
+
const dns = await import("node:dns");
|
|
1813
|
+
const addrs = await dns.promises.lookup(url.hostname, { all: true });
|
|
1814
|
+
if (!addrs.length) return null;
|
|
1815
|
+
for (const a of addrs) {
|
|
1816
|
+
if (isPrivateAddress(a.address)) return null;
|
|
1817
|
+
}
|
|
1818
|
+
} catch {
|
|
1819
|
+
return null;
|
|
1820
|
+
}
|
|
1821
|
+
let res;
|
|
1822
|
+
try {
|
|
1823
|
+
res = await fetch(url, { redirect: "manual" });
|
|
1824
|
+
} catch {
|
|
1825
|
+
return null;
|
|
1826
|
+
}
|
|
1827
|
+
if (res.status >= 300 && res.status < 400) {
|
|
1828
|
+
const loc = res.headers.get("location");
|
|
1829
|
+
if (!loc) return null;
|
|
1830
|
+
try {
|
|
1831
|
+
url = new URL(loc, url);
|
|
1832
|
+
} catch {
|
|
1833
|
+
return null;
|
|
1834
|
+
}
|
|
1835
|
+
continue;
|
|
1836
|
+
}
|
|
1837
|
+
if (!res.ok) return null;
|
|
1838
|
+
return Buffer.from(await res.arrayBuffer());
|
|
1839
|
+
}
|
|
1840
|
+
return null;
|
|
1841
|
+
}
|
|
1842
|
+
async function readSource(src) {
|
|
1843
|
+
if (isRemoteSource(src)) {
|
|
1844
|
+
const data = await safeFetchRemote(src);
|
|
1845
|
+
return data ? { data, mtime: 0 } : null;
|
|
1846
|
+
}
|
|
1847
|
+
let decoded;
|
|
1848
|
+
try {
|
|
1849
|
+
decoded = decodeURIComponent(src);
|
|
1850
|
+
} catch {
|
|
1851
|
+
return null;
|
|
1852
|
+
}
|
|
1853
|
+
const clean = (0, import_node_path2.normalize)(decoded).replace(/\\/g, "/");
|
|
1854
|
+
if (clean.includes("\0") || clean.includes("..")) return null;
|
|
1855
|
+
const file = (0, import_node_path2.resolve)(sourceDir, "." + (clean.startsWith("/") ? clean : "/" + clean));
|
|
1856
|
+
if (!file.startsWith(sourceDir + "/") && file !== sourceDir) return null;
|
|
1857
|
+
const dot = clean.split("/").pop() ?? "";
|
|
1858
|
+
if (dot.startsWith(".")) return null;
|
|
1859
|
+
if (!(0, import_node_fs.existsSync)(file)) return null;
|
|
1860
|
+
const st = (0, import_node_fs.statSync)(file);
|
|
1861
|
+
if (!st.isFile()) return null;
|
|
1862
|
+
return { data: (0, import_node_fs.readFileSync)(file), mtime: st.mtimeMs };
|
|
1863
|
+
}
|
|
1864
|
+
function cacheKey(src, w, h, q, f, mtime) {
|
|
1865
|
+
return (0, import_node_crypto2.createHash)("sha256").update(`${src}|${mtime}|${w}|${h}|${q}|${f}`).digest("hex").slice(0, 32);
|
|
1866
|
+
}
|
|
1867
|
+
async function processBuffer(data, w, h, q, format) {
|
|
1868
|
+
const sharp = await loadSharp();
|
|
1869
|
+
if (sharp && format) {
|
|
1870
|
+
try {
|
|
1871
|
+
let img = sharp(data);
|
|
1872
|
+
if (w || h) {
|
|
1873
|
+
img = img.resize(w || void 0, h || void 0, {
|
|
1874
|
+
fit: "inside",
|
|
1875
|
+
withoutEnlargement: true
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
if (format !== "jpeg" && format !== "png") {
|
|
1879
|
+
img = img.toFormat(format, { quality: q });
|
|
1880
|
+
} else if (format === "jpeg") {
|
|
1881
|
+
img = img.jpeg({ quality: q });
|
|
1882
|
+
}
|
|
1883
|
+
const out = await img.toBuffer();
|
|
1884
|
+
return { data: out, contentType: `image/${format}`, viaSharp: true };
|
|
1885
|
+
} catch (err2) {
|
|
1886
|
+
console.warn(` [tw/image] processing failed (${format}, ${w}x${h}), serving original: ${err2?.message ?? err2}`);
|
|
1887
|
+
return { data, contentType: "", viaSharp: false };
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
if (!warnedNoSharp) {
|
|
1891
|
+
warnedNoSharp = true;
|
|
1892
|
+
console.warn(" [tw/image] sharp not installed \u2014 serving original images (bun add sharp to enable resizing/WebP/AVIF)");
|
|
1893
|
+
}
|
|
1894
|
+
return { data, contentType: "", viaSharp: false };
|
|
1895
|
+
}
|
|
1896
|
+
async function handle(request) {
|
|
1897
|
+
const url = new URL(request.url);
|
|
1898
|
+
const pathname = url.pathname;
|
|
1899
|
+
if (!pathname.startsWith("/_tw/img/") && pathname !== "/_tw/img") return null;
|
|
1900
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
1901
|
+
return new Response("Method Not Allowed", { status: 405 });
|
|
1902
|
+
}
|
|
1903
|
+
const q = url.searchParams;
|
|
1904
|
+
let src;
|
|
1905
|
+
if (pathname === "/_tw/img/_remote") {
|
|
1906
|
+
src = q.get("src") ?? "";
|
|
1907
|
+
if (!src) return new Response("Not Found", { status: 404 });
|
|
1908
|
+
} else {
|
|
1909
|
+
src = pathname.slice("/_tw/img".length) || "/";
|
|
1910
|
+
if (src === "/" || src === "") return new Response("Not Found", { status: 404 });
|
|
1911
|
+
}
|
|
1912
|
+
const w = Math.max(0, Math.min(1e4, parseInt(q.get("w") ?? "0", 10) || 0));
|
|
1913
|
+
const h = Math.max(0, Math.min(1e4, parseInt(q.get("h") ?? "0", 10) || 0));
|
|
1914
|
+
const quality = Math.max(1, Math.min(100, parseInt(q.get("q") ?? String(config.quality), 10) || config.quality));
|
|
1915
|
+
const format = negotiateFormat(q.get("f"), request.headers.get("accept") ?? "");
|
|
1916
|
+
const source = await readSource(src);
|
|
1917
|
+
if (!source) return new Response("Not Found", { status: 404 });
|
|
1918
|
+
if ((0, import_node_path2.extname)(src).toLowerCase() === ".svg") {
|
|
1919
|
+
return new Response(new Uint8Array(source.data), {
|
|
1920
|
+
status: 200,
|
|
1921
|
+
headers: {
|
|
1922
|
+
"Content-Type": "image/svg+xml",
|
|
1923
|
+
"Cache-Control": `public, max-age=${config.cacheMaxAge}`,
|
|
1924
|
+
"X-TW-Image": "svg-passthrough"
|
|
1925
|
+
}
|
|
1926
|
+
});
|
|
1927
|
+
}
|
|
1928
|
+
if (!w && !h && !format) {
|
|
1929
|
+
return new Response(new Uint8Array(source.data), {
|
|
1930
|
+
status: 200,
|
|
1931
|
+
headers: {
|
|
1932
|
+
"Content-Type": contentTypeFor(src),
|
|
1933
|
+
"Cache-Control": `public, max-age=${config.cacheMaxAge}`,
|
|
1934
|
+
"X-TW-Image": "original"
|
|
1935
|
+
}
|
|
1936
|
+
});
|
|
1937
|
+
}
|
|
1938
|
+
const key = cacheKey(src, w, h, quality, format ?? "orig", source.mtime);
|
|
1939
|
+
const cacheFile = (0, import_node_path2.join)(cacheDir, `${key}${format ? "." + format : ""}`);
|
|
1940
|
+
const etag = `"${key}"`;
|
|
1941
|
+
if (request.headers.get("if-none-match") === etag) {
|
|
1942
|
+
return new Response(null, { status: 304, headers: { ETag: etag } });
|
|
1943
|
+
}
|
|
1944
|
+
if ((0, import_node_fs.existsSync)(cacheFile)) {
|
|
1945
|
+
const cached = (0, import_node_fs.readFileSync)(cacheFile);
|
|
1946
|
+
return new Response(new Uint8Array(cached), {
|
|
1947
|
+
status: 200,
|
|
1948
|
+
headers: {
|
|
1949
|
+
"Content-Type": contentTypeFor(cacheFile),
|
|
1950
|
+
"Cache-Control": `public, max-age=${config.cacheMaxAge}`,
|
|
1951
|
+
ETag: etag,
|
|
1952
|
+
"X-TW-Image": "cache"
|
|
1953
|
+
}
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
const result = await processBuffer(source.data, w, h, quality, format);
|
|
1957
|
+
if (result.viaSharp) {
|
|
1958
|
+
try {
|
|
1959
|
+
(0, import_node_fs.mkdirSync)(cacheDir, { recursive: true });
|
|
1960
|
+
(0, import_node_fs.writeFileSync)(cacheFile, result.data);
|
|
1961
|
+
} catch {
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
return new Response(new Uint8Array(result.data), {
|
|
1965
|
+
status: 200,
|
|
1966
|
+
headers: {
|
|
1967
|
+
"Content-Type": result.contentType || contentTypeFor(src),
|
|
1968
|
+
"Cache-Control": `public, max-age=${config.cacheMaxAge}`,
|
|
1969
|
+
ETag: etag,
|
|
1970
|
+
"X-TW-Image": result.viaSharp ? "optimized" : "passthrough"
|
|
1971
|
+
}
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
return { handle, sourceDir, cacheDir, config };
|
|
1975
|
+
}
|
|
1976
|
+
var import_node_crypto2, import_node_fs, import_node_path2, import_node_url2, DEFAULT_IMAGE_CONFIG, IMAGE_EXTENSIONS, SHARP_FORMATS;
|
|
1977
|
+
var init_tw2 = __esm({
|
|
1978
|
+
"../../packages/image/tw/index.ts"() {
|
|
1979
|
+
import_node_crypto2 = require("node:crypto");
|
|
1980
|
+
import_node_fs = require("node:fs");
|
|
1981
|
+
import_node_path2 = require("node:path");
|
|
1982
|
+
import_node_url2 = require("node:url");
|
|
1983
|
+
DEFAULT_IMAGE_CONFIG = {
|
|
1984
|
+
quality: 75,
|
|
1985
|
+
formats: ["avif", "webp"],
|
|
1986
|
+
breakpoints: [320, 640, 768, 1024, 1280, 1920],
|
|
1987
|
+
remoteAllowHosts: [],
|
|
1988
|
+
cacheMaxAge: 86400
|
|
1989
|
+
};
|
|
1990
|
+
IMAGE_EXTENSIONS = {
|
|
1991
|
+
".jpg": "image/jpeg",
|
|
1992
|
+
".jpeg": "image/jpeg",
|
|
1993
|
+
".png": "image/png",
|
|
1994
|
+
".webp": "image/webp",
|
|
1995
|
+
".avif": "image/avif",
|
|
1996
|
+
".gif": "image/gif",
|
|
1997
|
+
".svg": "image/svg+xml",
|
|
1998
|
+
".ico": "image/x-icon"
|
|
1999
|
+
};
|
|
2000
|
+
SHARP_FORMATS = {
|
|
2001
|
+
avif: "avif",
|
|
2002
|
+
webp: "webp",
|
|
2003
|
+
jpeg: "jpeg",
|
|
2004
|
+
jpg: "jpeg",
|
|
2005
|
+
png: "png"
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
});
|
|
2009
|
+
|
|
2010
|
+
// ../../packages/server/tw/websocket-manager.ts
|
|
2011
|
+
var DEFAULT_MAX_ROOMS, DEFAULT_MAX_ROOM_SIZE, WebSocketManager;
|
|
2012
|
+
var init_websocket_manager = __esm({
|
|
2013
|
+
"../../packages/server/tw/websocket-manager.ts"() {
|
|
2014
|
+
DEFAULT_MAX_ROOMS = 1e3;
|
|
2015
|
+
DEFAULT_MAX_ROOM_SIZE = 1e4;
|
|
2016
|
+
WebSocketManager = class {
|
|
2017
|
+
connections = /* @__PURE__ */ new Map();
|
|
2018
|
+
meta = /* @__PURE__ */ new Map();
|
|
2019
|
+
rooms = /* @__PURE__ */ new Map();
|
|
2020
|
+
nextId = 1;
|
|
2021
|
+
maxRooms;
|
|
2022
|
+
maxRoomSize;
|
|
2023
|
+
constructor(options = {}) {
|
|
2024
|
+
this.maxRooms = options.maxRooms ?? DEFAULT_MAX_ROOMS;
|
|
2025
|
+
this.maxRoomSize = options.maxRoomSize ?? DEFAULT_MAX_ROOM_SIZE;
|
|
2026
|
+
}
|
|
2027
|
+
/** Register a socket on open; returns the assigned connection id. */
|
|
2028
|
+
add(connection, meta = {}) {
|
|
2029
|
+
let id = connection?.data?.id ?? connection?.id;
|
|
2030
|
+
if (!id) {
|
|
2031
|
+
id = "ws-" + process.pid + "-" + this.nextId++;
|
|
2032
|
+
}
|
|
2033
|
+
if (connection && typeof connection === "object") {
|
|
2034
|
+
if (!connection.data) connection.data = { id };
|
|
2035
|
+
else if (!connection.data.id) connection.data.id = id;
|
|
2036
|
+
if (!connection.id) connection.id = id;
|
|
2037
|
+
}
|
|
2038
|
+
this.connections.set(id, connection);
|
|
2039
|
+
this.meta.set(id, meta);
|
|
2040
|
+
return id;
|
|
2041
|
+
}
|
|
2042
|
+
/** Lookup by connection id. */
|
|
2043
|
+
get(id) {
|
|
2044
|
+
return this.connections.get(id);
|
|
2045
|
+
}
|
|
2046
|
+
/** Deregister on close; auto-leaves rooms (last member out kills the room). */
|
|
2047
|
+
remove(id) {
|
|
2048
|
+
for (const room of this.rooms.values()) {
|
|
2049
|
+
room.members.delete(id);
|
|
2050
|
+
if (room.members.size === 0) this.rooms.delete(room.name);
|
|
2051
|
+
}
|
|
2052
|
+
this.meta.delete(id);
|
|
2053
|
+
return this.connections.delete(id);
|
|
2054
|
+
}
|
|
2055
|
+
/** Serialize once and send to every socket regardless of room membership. */
|
|
2056
|
+
broadcast(payload) {
|
|
2057
|
+
const data = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
2058
|
+
for (const conn of this.connections.values()) {
|
|
2059
|
+
this.safeSend(conn, data);
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
/** Send to one connection. */
|
|
2063
|
+
send(id, payload) {
|
|
2064
|
+
const conn = this.connections.get(id);
|
|
2065
|
+
if (!conn) return false;
|
|
2066
|
+
this.safeSend(conn, typeof payload === "string" ? payload : JSON.stringify(payload));
|
|
2067
|
+
return true;
|
|
2068
|
+
}
|
|
2069
|
+
/** All live entries. */
|
|
2070
|
+
getConnections() {
|
|
2071
|
+
return [...this.connections.keys()].map((id) => ({ id, meta: this.meta.get(id) ?? {} }));
|
|
2072
|
+
}
|
|
2073
|
+
/** Explicit pre-creation with metadata (rooms also auto-create on join). */
|
|
2074
|
+
createRoom(name, metadata = {}) {
|
|
2075
|
+
if (this.rooms.has(name)) return;
|
|
2076
|
+
if (this.rooms.size >= this.maxRooms) {
|
|
2077
|
+
throw new Error(`maxRooms reached (${this.maxRooms}): cannot create "${name}"`);
|
|
2078
|
+
}
|
|
2079
|
+
this.rooms.set(name, { name, metadata, members: /* @__PURE__ */ new Set() });
|
|
2080
|
+
}
|
|
2081
|
+
/** Join a room (auto-creates). A full room refuses joins. */
|
|
2082
|
+
joinRoom(connectionId, name) {
|
|
2083
|
+
if (!this.connections.has(connectionId)) return false;
|
|
2084
|
+
let room = this.rooms.get(name);
|
|
2085
|
+
if (!room) {
|
|
2086
|
+
if (this.rooms.size >= this.maxRooms) {
|
|
2087
|
+
throw new Error(`maxRooms reached (${this.maxRooms}): cannot create "${name}"`);
|
|
2088
|
+
}
|
|
2089
|
+
room = { name, metadata: {}, members: /* @__PURE__ */ new Set() };
|
|
2090
|
+
this.rooms.set(name, room);
|
|
2091
|
+
}
|
|
2092
|
+
if (room.members.size >= this.maxRoomSize) return false;
|
|
2093
|
+
room.members.add(connectionId);
|
|
2094
|
+
return true;
|
|
2095
|
+
}
|
|
2096
|
+
/** Leave a room; the room disappears when its last member leaves. */
|
|
2097
|
+
leaveRoom(connectionId, name) {
|
|
2098
|
+
const room = this.rooms.get(name);
|
|
2099
|
+
if (!room) return false;
|
|
2100
|
+
const had = room.members.delete(connectionId);
|
|
2101
|
+
if (room.members.size === 0) this.rooms.delete(name);
|
|
2102
|
+
return had;
|
|
2103
|
+
}
|
|
2104
|
+
/** Iterate only that room's members — Set membership guarantees exactly one copy. */
|
|
2105
|
+
broadcastToRoom(name, payload) {
|
|
2106
|
+
const room = this.rooms.get(name);
|
|
2107
|
+
if (!room) return 0;
|
|
2108
|
+
const data = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
2109
|
+
let sent = 0;
|
|
2110
|
+
for (const id of room.members) {
|
|
2111
|
+
const conn = this.connections.get(id);
|
|
2112
|
+
if (this.safeSend(conn, data)) sent++;
|
|
2113
|
+
}
|
|
2114
|
+
return sent;
|
|
2115
|
+
}
|
|
2116
|
+
/** Room + members. */
|
|
2117
|
+
getRoom(name) {
|
|
2118
|
+
const room = this.rooms.get(name);
|
|
2119
|
+
if (!room) return void 0;
|
|
2120
|
+
return { name: room.name, metadata: room.metadata, members: [...room.members] };
|
|
2121
|
+
}
|
|
2122
|
+
/** All rooms. */
|
|
2123
|
+
getRooms() {
|
|
2124
|
+
return [...this.rooms.values()].map((r) => ({ name: r.name, members: r.members.size }));
|
|
2125
|
+
}
|
|
2126
|
+
safeSend(conn, data) {
|
|
2127
|
+
try {
|
|
2128
|
+
if (!conn || typeof conn?.send !== "function") return false;
|
|
2129
|
+
if (typeof conn.readyState === "number" && conn.readyState !== 1) return false;
|
|
2130
|
+
conn.send(data);
|
|
2131
|
+
return true;
|
|
2132
|
+
} catch {
|
|
2133
|
+
return false;
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
};
|
|
1928
2137
|
}
|
|
1929
2138
|
});
|
|
1930
2139
|
|
|
1931
|
-
// ../../packages/
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
2140
|
+
// ../../packages/server/tw/routing/layout-chain.ts
|
|
2141
|
+
function resolveLayoutChain(matchResult) {
|
|
2142
|
+
const { node, chain, params } = matchResult;
|
|
2143
|
+
const layouts = [];
|
|
2144
|
+
const templates = [];
|
|
2145
|
+
for (const nodeInChain of chain) {
|
|
2146
|
+
const layoutFile = nodeInChain.files.find((f) => f.type === "layout");
|
|
2147
|
+
if (layoutFile) layouts.push(layoutFile);
|
|
2148
|
+
const templateFile = nodeInChain.files.find((f) => f.type === "template");
|
|
2149
|
+
if (templateFile) templates.push(templateFile);
|
|
1935
2150
|
}
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
2151
|
+
let loading;
|
|
2152
|
+
let error;
|
|
2153
|
+
let notFound2;
|
|
2154
|
+
let head;
|
|
2155
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
2156
|
+
if (!loading) {
|
|
2157
|
+
loading = chain[i].files.find((f) => f.type === "loading");
|
|
2158
|
+
}
|
|
2159
|
+
if (!error) {
|
|
2160
|
+
error = chain[i].files.find((f) => f.type === "error");
|
|
2161
|
+
}
|
|
2162
|
+
if (!notFound2) {
|
|
2163
|
+
notFound2 = chain[i].files.find((f) => f.type === "not-found");
|
|
2164
|
+
}
|
|
2165
|
+
if (!head) {
|
|
2166
|
+
head = chain[i].files.find((f) => f.type === "head");
|
|
2167
|
+
}
|
|
1941
2168
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
"../../packages/shared/tw/hashing/index.ts"() {
|
|
1947
|
-
init_crc32();
|
|
1948
|
-
init_etag();
|
|
1949
|
-
init_hmac();
|
|
1950
|
-
init_md5();
|
|
1951
|
-
init_merkle();
|
|
1952
|
-
init_sha();
|
|
1953
|
-
init_uuid();
|
|
2169
|
+
const page = node.files.find((f) => f.type === "page");
|
|
2170
|
+
let renderMode = "ssr";
|
|
2171
|
+
if (page) {
|
|
2172
|
+
renderMode = extractRenderMode(page);
|
|
1954
2173
|
}
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
2174
|
+
return {
|
|
2175
|
+
node,
|
|
2176
|
+
chain,
|
|
2177
|
+
params,
|
|
2178
|
+
layouts,
|
|
2179
|
+
templates,
|
|
2180
|
+
loading,
|
|
2181
|
+
error,
|
|
2182
|
+
notFound: notFound2,
|
|
2183
|
+
head,
|
|
2184
|
+
page,
|
|
2185
|
+
middleware: [],
|
|
2186
|
+
renderMode
|
|
2187
|
+
};
|
|
1969
2188
|
}
|
|
1970
|
-
function
|
|
1971
|
-
|
|
1972
|
-
if (!m) return null;
|
|
1973
|
-
return { levels: m[1].length, target: m[2] };
|
|
2189
|
+
function findGlobalError(rootNode) {
|
|
2190
|
+
return rootNode.files.find((f) => f.type === "global-error");
|
|
1974
2191
|
}
|
|
1975
|
-
function
|
|
1976
|
-
|
|
1977
|
-
if (optCatchAll) return { type: "optional-catch-all", name: optCatchAll[1] };
|
|
1978
|
-
const catchAll = name.match(/^\[\.\.\.([^\]]+)\]$/);
|
|
1979
|
-
if (catchAll) return { type: "catch-all", name: catchAll[1] };
|
|
1980
|
-
const dynamic = name.match(/^\[([^\]]+)\]$/);
|
|
1981
|
-
if (dynamic) return { type: "dynamic", name: dynamic[1] };
|
|
1982
|
-
return { type: "static", name };
|
|
2192
|
+
function findRootError(rootNode) {
|
|
2193
|
+
return (rootNode.files || []).find((f) => f.type === "error");
|
|
1983
2194
|
}
|
|
1984
|
-
function
|
|
1985
|
-
|
|
1986
|
-
return name.slice(1);
|
|
2195
|
+
function findRootNotFound(rootNode) {
|
|
2196
|
+
return rootNode.files.find((f) => f.type === "not-found");
|
|
1987
2197
|
}
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
// Intercepting routes (docs/intercepting-routes.md): a `(.)page.tw`
|
|
1998
|
-
// beside a page renders INSTEAD of it on SPA navigation (X-TW-Navigate).
|
|
1999
|
-
"(.)page": "intercept-page",
|
|
2000
|
-
"layout": "layout",
|
|
2001
|
-
"template": "template",
|
|
2002
|
-
"loading": "loading",
|
|
2003
|
-
"error": "error",
|
|
2004
|
-
"not-found": "not-found",
|
|
2005
|
-
"global-error": "global-error",
|
|
2006
|
-
"default": "default",
|
|
2007
|
-
"head": "head",
|
|
2008
|
-
"route": "route",
|
|
2009
|
-
"middleware": "middleware"
|
|
2010
|
-
};
|
|
2011
|
-
}
|
|
2012
|
-
});
|
|
2013
|
-
|
|
2014
|
-
// ../../packages/shared/tw/routing/index.ts
|
|
2015
|
-
var init_routing = __esm({
|
|
2016
|
-
"../../packages/shared/tw/routing/index.ts"() {
|
|
2017
|
-
init_constants2();
|
|
2018
|
-
}
|
|
2019
|
-
});
|
|
2020
|
-
|
|
2021
|
-
// ../../packages/shared/tw/types/common.ts
|
|
2022
|
-
var init_common = __esm({
|
|
2023
|
-
"../../packages/shared/tw/types/common.ts"() {
|
|
2024
|
-
}
|
|
2025
|
-
});
|
|
2026
|
-
|
|
2027
|
-
// ../../packages/shared/tw/types/errors.ts
|
|
2028
|
-
var init_errors = __esm({
|
|
2029
|
-
"../../packages/shared/tw/types/errors.ts"() {
|
|
2030
|
-
}
|
|
2031
|
-
});
|
|
2032
|
-
|
|
2033
|
-
// ../../packages/shared/tw/types/results.ts
|
|
2034
|
-
var init_results = __esm({
|
|
2035
|
-
"../../packages/shared/tw/types/results.ts"() {
|
|
2198
|
+
function collectParallelDefaults(node) {
|
|
2199
|
+
const defaults = /* @__PURE__ */ new Map();
|
|
2200
|
+
for (const child of node.children) {
|
|
2201
|
+
if (child.kind === "parallel" && child.slotName) {
|
|
2202
|
+
const defaultFile = child.files.find((f) => f.type === "default");
|
|
2203
|
+
if (defaultFile) {
|
|
2204
|
+
defaults.set(child.slotName, defaultFile);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2036
2207
|
}
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2208
|
+
return defaults;
|
|
2209
|
+
}
|
|
2210
|
+
function collectParallelPages(node, pathParts, index) {
|
|
2211
|
+
const pages = /* @__PURE__ */ new Map();
|
|
2212
|
+
for (const child of node.children) {
|
|
2213
|
+
if (child.kind === "parallel" && child.slotName) {
|
|
2214
|
+
const pageFile = child.files.find((f) => f.type === "page");
|
|
2215
|
+
if (pageFile) {
|
|
2216
|
+
pages.set(child.slotName, pageFile);
|
|
2217
|
+
}
|
|
2218
|
+
if (index < pathParts.length) {
|
|
2219
|
+
for (const grandchild of child.children) {
|
|
2220
|
+
if (grandchild.kind === "static" && grandchild.segment === pathParts[index]) {
|
|
2221
|
+
const deepPage = grandchild.files.find((f) => f.type === "page");
|
|
2222
|
+
if (deepPage) {
|
|
2223
|
+
pages.set(child.slotName, deepPage);
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2045
2229
|
}
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2230
|
+
return pages;
|
|
2231
|
+
}
|
|
2232
|
+
function extractRenderMode(page) {
|
|
2233
|
+
try {
|
|
2234
|
+
const fs = require("node:fs");
|
|
2235
|
+
const source = fs.readFileSync(page.absolutePath, "utf-8");
|
|
2236
|
+
const m = source.match(/render\s+(static|ssr|island|edge)/);
|
|
2237
|
+
if (m) {
|
|
2238
|
+
return m[1];
|
|
2239
|
+
}
|
|
2240
|
+
} catch {
|
|
2051
2241
|
}
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
"../../packages/shared/tw/index.ts"() {
|
|
2057
|
-
init_sha();
|
|
2058
|
-
init_utils();
|
|
2059
|
-
init_config();
|
|
2060
|
-
init_constants();
|
|
2061
|
-
init_crypto();
|
|
2062
|
-
init_env();
|
|
2063
|
-
init_fs();
|
|
2064
|
-
init_hashing();
|
|
2065
|
-
init_logger2();
|
|
2066
|
-
init_routing();
|
|
2067
|
-
init_types();
|
|
2068
|
-
init_transform();
|
|
2242
|
+
return "ssr";
|
|
2243
|
+
}
|
|
2244
|
+
var init_layout_chain = __esm({
|
|
2245
|
+
"../../packages/server/tw/routing/layout-chain.ts"() {
|
|
2069
2246
|
}
|
|
2070
2247
|
});
|
|
2071
2248
|
|
|
@@ -2416,7 +2593,7 @@ function readNestedBlocks(source, start, end) {
|
|
|
2416
2593
|
}
|
|
2417
2594
|
var init_blocks = __esm({
|
|
2418
2595
|
"../../packages/compiler/tw/lexer/blocks.ts"() {
|
|
2419
|
-
|
|
2596
|
+
init_tw();
|
|
2420
2597
|
}
|
|
2421
2598
|
});
|
|
2422
2599
|
|
|
@@ -7750,6 +7927,12 @@ function parsePageDirective(cursor, token) {
|
|
|
7750
7927
|
if (!optToken) break;
|
|
7751
7928
|
const optName = optToken.value;
|
|
7752
7929
|
cursor.skipWhitespace();
|
|
7930
|
+
if (optName === "cache" && cursor.peek() && cursor.peek().type === "LBRACE") {
|
|
7931
|
+
cursor.advance();
|
|
7932
|
+
options.cache = parseCacheBlockOptions(cursor, token);
|
|
7933
|
+
cursor.skipWhitespace();
|
|
7934
|
+
continue;
|
|
7935
|
+
}
|
|
7753
7936
|
const nextToken = cursor.peek();
|
|
7754
7937
|
if (nextToken && (nextToken.type === "STRING" || nextToken.type === "NUMBER" || nextToken.type === "IDENT" || nextToken.type === "KEYWORD")) {
|
|
7755
7938
|
cursor.advance();
|
|
@@ -7787,6 +7970,42 @@ function parsePageDirective(cursor, token) {
|
|
|
7787
7970
|
col: token.pos.col
|
|
7788
7971
|
};
|
|
7789
7972
|
}
|
|
7973
|
+
function parseCacheBlockOptions(cursor, pageToken) {
|
|
7974
|
+
const out = {};
|
|
7975
|
+
cursor.skipWhitespace();
|
|
7976
|
+
while (cursor.peek() && cursor.peek().type !== "RBRACE" && cursor.peek().type !== "EOF") {
|
|
7977
|
+
const keyToken = cursor.advance();
|
|
7978
|
+
if (!keyToken) break;
|
|
7979
|
+
const key = keyToken.value;
|
|
7980
|
+
cursor.skipWhitespace();
|
|
7981
|
+
cursor.match("EQUALS") || cursor.match("COLON");
|
|
7982
|
+
cursor.skipWhitespace();
|
|
7983
|
+
const valToken = cursor.peek();
|
|
7984
|
+
if (valToken && (valToken.type === "STRING" || valToken.type === "NUMBER")) {
|
|
7985
|
+
cursor.advance();
|
|
7986
|
+
out[key] = valToken.type === "NUMBER" ? parseFloat(valToken.value) : valToken.value;
|
|
7987
|
+
} else if (valToken && (valToken.type === "IDENT" || valToken.type === "KEYWORD")) {
|
|
7988
|
+
cursor.advance();
|
|
7989
|
+
out[key] = valToken.value;
|
|
7990
|
+
}
|
|
7991
|
+
cursor.skipWhitespace();
|
|
7992
|
+
cursor.match("COMMA");
|
|
7993
|
+
cursor.skipWhitespace();
|
|
7994
|
+
}
|
|
7995
|
+
cursor.consumeIf("RBRACE");
|
|
7996
|
+
if (out.revalidate == null && out.life == null) {
|
|
7997
|
+
cursor.errors.add({
|
|
7998
|
+
type: "error",
|
|
7999
|
+
message: 'cache { } requires a `revalidate N` window or a `life "profile"` (docs/cache-tags.md)',
|
|
8000
|
+
line: pageToken.pos.line,
|
|
8001
|
+
col: pageToken.pos.col,
|
|
8002
|
+
code: "TW090",
|
|
8003
|
+
severity: "error",
|
|
8004
|
+
category: "syntax"
|
|
8005
|
+
});
|
|
8006
|
+
}
|
|
8007
|
+
return out;
|
|
8008
|
+
}
|
|
7790
8009
|
function parseHeadDirective(cursor, token, parseBody) {
|
|
7791
8010
|
let title;
|
|
7792
8011
|
const meta = [];
|
|
@@ -8540,7 +8759,7 @@ function parseBlockBody(cursor, parseBody) {
|
|
|
8540
8759
|
var DIRECTIVE_KEYWORDS, SIGNAL_FUNCTIONS;
|
|
8541
8760
|
var init_directives = __esm({
|
|
8542
8761
|
"../../packages/compiler/tw/parser/directives/index.ts"() {
|
|
8543
|
-
|
|
8762
|
+
init_tw();
|
|
8544
8763
|
DIRECTIVE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
8545
8764
|
"page",
|
|
8546
8765
|
"head",
|
|
@@ -9861,7 +10080,7 @@ var init_statements = __esm({
|
|
|
9861
10080
|
init_nodes();
|
|
9862
10081
|
init_recovery();
|
|
9863
10082
|
init_directives();
|
|
9864
|
-
|
|
10083
|
+
init_tw();
|
|
9865
10084
|
CLASS_BOUNDARY_BOOLEAN_ATTRS = /* @__PURE__ */ new Set([
|
|
9866
10085
|
"disabled",
|
|
9867
10086
|
"required",
|
|
@@ -11736,7 +11955,7 @@ var init_parser = __esm({
|
|
|
11736
11955
|
init_recovery();
|
|
11737
11956
|
init_statements();
|
|
11738
11957
|
init_directives();
|
|
11739
|
-
|
|
11958
|
+
init_tw();
|
|
11740
11959
|
init_parser_recovery();
|
|
11741
11960
|
init_precedence_table();
|
|
11742
11961
|
init_props();
|
|
@@ -17665,7 +17884,7 @@ var init_types4 = __esm({
|
|
|
17665
17884
|
var TW_VERSION, TW_GENERATOR_META;
|
|
17666
17885
|
var init_version = __esm({
|
|
17667
17886
|
"../../packages/compiler/tw/codegen/version.ts"() {
|
|
17668
|
-
TW_VERSION = "1.0.
|
|
17887
|
+
TW_VERSION = "1.0.6";
|
|
17669
17888
|
TW_GENERATOR_META = `<meta name="generator" content="TW Framework ${TW_VERSION}">`;
|
|
17670
17889
|
}
|
|
17671
17890
|
});
|
|
@@ -17831,7 +18050,7 @@ function generateBuiltinTag(specifier, props, textContent) {
|
|
|
17831
18050
|
var BUILTIN_COMPONENT_SPECIFIERS, imageConfig;
|
|
17832
18051
|
var init_builtin_components = __esm({
|
|
17833
18052
|
"../../packages/compiler/tw/codegen/builtin-components.ts"() {
|
|
17834
|
-
|
|
18053
|
+
init_tw2();
|
|
17835
18054
|
init_tw3();
|
|
17836
18055
|
BUILTIN_COMPONENT_SPECIFIERS = ["@tw/optImage", "@tw/RouterLink"];
|
|
17837
18056
|
imageConfig = DEFAULT_IMAGE_CONFIG;
|
|
@@ -23871,7 +24090,12 @@ var init_codes = __esm({
|
|
|
23871
24090
|
TW086: { message: "Deprecated state declaration syntax", severity: "warning", category: "deprecation" },
|
|
23872
24091
|
TW087: { message: "Deprecated component lifecycle hook", severity: "warning", category: "deprecation" },
|
|
23873
24092
|
TW088: { message: "Deprecated import path", severity: "warning", category: "deprecation" },
|
|
23874
|
-
TW089: { message: "Deprecated render mode -- use 'interactive'", severity: "warning", category: "deprecation" }
|
|
24093
|
+
TW089: { message: "Deprecated render mode -- use 'interactive'", severity: "warning", category: "deprecation" },
|
|
24094
|
+
// Cache (docs/cache-tags.md)
|
|
24095
|
+
TW090: { message: "cache { } requires a revalidate window or a life profile", severity: "error", category: "syntax" },
|
|
24096
|
+
TW091: { message: "fn cached handler is impure (request.cookies/headers/body or setSignal)", severity: "error", category: "semantic" },
|
|
24097
|
+
TW092: { message: "Unknown cache profile", severity: "error", category: "semantic" },
|
|
24098
|
+
TW093: { message: "Non-deterministic call in a cached handler (value freezes into the cache entry)", severity: "warning", category: "best-practice" }
|
|
23875
24099
|
};
|
|
23876
24100
|
}
|
|
23877
24101
|
});
|
|
@@ -24970,6 +25194,7 @@ var init_rules = __esm({
|
|
|
24970
25194
|
"row",
|
|
24971
25195
|
"rowgroup",
|
|
24972
25196
|
"rowheader",
|
|
25197
|
+
"table",
|
|
24973
25198
|
"scrollbar",
|
|
24974
25199
|
"search",
|
|
24975
25200
|
"searchbox",
|
|
@@ -25001,6 +25226,10 @@ var init_rules = __esm({
|
|
|
25001
25226
|
"aria-atomic",
|
|
25002
25227
|
"aria-autocomplete",
|
|
25003
25228
|
"aria-busy",
|
|
25229
|
+
"aria-braillelabel",
|
|
25230
|
+
"aria-brailleroledescription",
|
|
25231
|
+
"aria-description",
|
|
25232
|
+
"aria-details",
|
|
25004
25233
|
"aria-checked",
|
|
25005
25234
|
"aria-colcount",
|
|
25006
25235
|
"aria-colindex",
|
|
@@ -28559,7 +28788,7 @@ var init_disk = __esm({
|
|
|
28559
28788
|
"../../packages/compiler/tw/cache/disk.ts"() {
|
|
28560
28789
|
import_node_fs3 = require("node:fs");
|
|
28561
28790
|
import_node_path4 = require("node:path");
|
|
28562
|
-
|
|
28791
|
+
init_tw();
|
|
28563
28792
|
DiskCache = class {
|
|
28564
28793
|
constructor(cacheDir) {
|
|
28565
28794
|
this.cacheDir = cacheDir;
|
|
@@ -28627,7 +28856,7 @@ var init_disk = __esm({
|
|
|
28627
28856
|
|
|
28628
28857
|
// ../../packages/compiler/tw/cache/cache.ts
|
|
28629
28858
|
var UnifiedCache;
|
|
28630
|
-
var
|
|
28859
|
+
var init_cache2 = __esm({
|
|
28631
28860
|
"../../packages/compiler/tw/cache/cache.ts"() {
|
|
28632
28861
|
init_memory();
|
|
28633
28862
|
init_disk();
|
|
@@ -28692,14 +28921,14 @@ function makeConfigKey(config) {
|
|
|
28692
28921
|
}
|
|
28693
28922
|
var init_keys = __esm({
|
|
28694
28923
|
"../../packages/compiler/tw/cache/keys.ts"() {
|
|
28695
|
-
|
|
28924
|
+
init_tw();
|
|
28696
28925
|
}
|
|
28697
28926
|
});
|
|
28698
28927
|
|
|
28699
28928
|
// ../../packages/compiler/tw/cache/index.ts
|
|
28700
|
-
var
|
|
28929
|
+
var init_cache3 = __esm({
|
|
28701
28930
|
"../../packages/compiler/tw/cache/index.ts"() {
|
|
28702
|
-
|
|
28931
|
+
init_cache2();
|
|
28703
28932
|
init_disk();
|
|
28704
28933
|
init_keys();
|
|
28705
28934
|
init_memory();
|
|
@@ -29074,7 +29303,7 @@ function removeFile(graph, file) {
|
|
|
29074
29303
|
}
|
|
29075
29304
|
var init_incremental = __esm({
|
|
29076
29305
|
"../../packages/compiler/tw/incremental/index.ts"() {
|
|
29077
|
-
|
|
29306
|
+
init_tw();
|
|
29078
29307
|
}
|
|
29079
29308
|
});
|
|
29080
29309
|
|
|
@@ -34530,7 +34759,7 @@ var init_tw4 = __esm({
|
|
|
34530
34759
|
init_server_components();
|
|
34531
34760
|
init_suspense();
|
|
34532
34761
|
init_ast();
|
|
34533
|
-
|
|
34762
|
+
init_cache3();
|
|
34534
34763
|
init_codegen();
|
|
34535
34764
|
init_codegen();
|
|
34536
34765
|
init_diagnostics();
|
|
@@ -34900,7 +35129,7 @@ var init_scanner = __esm({
|
|
|
34900
35129
|
"../../packages/server/tw/routing/scanner.ts"() {
|
|
34901
35130
|
import_node_fs5 = require("node:fs");
|
|
34902
35131
|
import_node_path6 = require("node:path");
|
|
34903
|
-
|
|
35132
|
+
init_tw();
|
|
34904
35133
|
}
|
|
34905
35134
|
});
|
|
34906
35135
|
|
|
@@ -34962,16 +35191,26 @@ var init_helpers = __esm({
|
|
|
34962
35191
|
});
|
|
34963
35192
|
|
|
34964
35193
|
// ../../packages/server/tw/routing/render-pipeline.ts
|
|
35194
|
+
function getCacheProfiles(rootDir) {
|
|
35195
|
+
if (__profilesCache && __profilesCache.rootDir === rootDir) return __profilesCache.profiles;
|
|
35196
|
+
let profiles = {};
|
|
35197
|
+
try {
|
|
35198
|
+
profiles = readCacheProfilesSync(rootDir);
|
|
35199
|
+
} catch {
|
|
35200
|
+
}
|
|
35201
|
+
__profilesCache = { rootDir, profiles };
|
|
35202
|
+
return profiles;
|
|
35203
|
+
}
|
|
34965
35204
|
function createRenderPipeline(opts) {
|
|
34966
35205
|
return new RenderPipeline(opts);
|
|
34967
35206
|
}
|
|
34968
|
-
var import_node_fs6, import_node_path7, RenderPipeline;
|
|
35207
|
+
var import_node_fs6, import_node_path7, __profilesCache, RenderPipeline;
|
|
34969
35208
|
var init_render_pipeline = __esm({
|
|
34970
35209
|
"../../packages/server/tw/routing/render-pipeline.ts"() {
|
|
34971
35210
|
import_node_fs6 = require("node:fs");
|
|
34972
35211
|
import_node_path7 = require("node:path");
|
|
34973
35212
|
init_tw4();
|
|
34974
|
-
|
|
35213
|
+
init_tw();
|
|
34975
35214
|
init_twm_loader();
|
|
34976
35215
|
init_scanner();
|
|
34977
35216
|
init_layout_chain();
|
|
@@ -34979,6 +35218,7 @@ var init_render_pipeline = __esm({
|
|
|
34979
35218
|
init_tw6();
|
|
34980
35219
|
init_twm_loader();
|
|
34981
35220
|
init_tw6();
|
|
35221
|
+
__profilesCache = null;
|
|
34982
35222
|
RenderPipeline = class {
|
|
34983
35223
|
rootDir;
|
|
34984
35224
|
homeDir;
|
|
@@ -34989,6 +35229,17 @@ var init_render_pipeline = __esm({
|
|
|
34989
35229
|
dev;
|
|
34990
35230
|
routeTree = null;
|
|
34991
35231
|
compiledCache = /* @__PURE__ */ new Map();
|
|
35232
|
+
/**
|
|
35233
|
+
* Render cache (docs/cache-tags.md). Entry fields:
|
|
35234
|
+
* expiresAt -- v1.0.5 compat: fresh-until timestamp (same as freshUntil)
|
|
35235
|
+
* freshUntil -- age < freshUntil -> HIT (zero work)
|
|
35236
|
+
* expireAt -- freshUntil <= age < expireAt -> STALE + background
|
|
35237
|
+
* refresh; age >= expireAt -> dropped (blocking MISS)
|
|
35238
|
+
* cache -- resolved cache config (null for plain TTL entries); its
|
|
35239
|
+
* `tag` is the single source of truth for revalidateTag() -- the tag
|
|
35240
|
+
* "index" is derived by scanning entries (<= 100), which is the
|
|
35241
|
+
* documented lazy rebuild (design 6.3).
|
|
35242
|
+
*/
|
|
34992
35243
|
renderCache = /* @__PURE__ */ new Map();
|
|
34993
35244
|
constructor(opts) {
|
|
34994
35245
|
this.rootDir = opts.rootDir;
|
|
@@ -35035,15 +35286,18 @@ var init_render_pipeline = __esm({
|
|
|
35035
35286
|
const cacheKey = sha256(`${pathname}:${JSON.stringify(stateVars ?? {})}:${this.navRequest ? "nav" : ""}`);
|
|
35036
35287
|
if (this.enableCache) {
|
|
35037
35288
|
const cached = this.renderCache.get(cacheKey);
|
|
35038
|
-
|
|
35289
|
+
const now = Date.now();
|
|
35290
|
+
const freshUntil = cached ? cached.freshUntil ?? cached.expiresAt : 0;
|
|
35291
|
+
const expireAt = cached ? cached.expireAt ?? freshUntil : 0;
|
|
35292
|
+
if (cached && now < freshUntil) {
|
|
35039
35293
|
return {
|
|
35040
35294
|
...cached.result,
|
|
35041
35295
|
fromCache: true,
|
|
35042
35296
|
durationMs: 0,
|
|
35043
|
-
headers:
|
|
35297
|
+
headers: this.stampCacheHeaders(cached.result.headers, cached, now, "HIT")
|
|
35044
35298
|
};
|
|
35045
35299
|
}
|
|
35046
|
-
if (cached && cached.
|
|
35300
|
+
if (cached && now < expireAt && cached.swr && !this.pendingRefresh.has(cacheKey)) {
|
|
35047
35301
|
this.pendingRefresh.add(cacheKey);
|
|
35048
35302
|
void Promise.resolve().then(() => {
|
|
35049
35303
|
try {
|
|
@@ -35058,9 +35312,10 @@ var init_render_pipeline = __esm({
|
|
|
35058
35312
|
fromCache: true,
|
|
35059
35313
|
stale: true,
|
|
35060
35314
|
durationMs: 0,
|
|
35061
|
-
headers:
|
|
35315
|
+
headers: this.stampCacheHeaders(cached.result.headers, cached, now, "STALE")
|
|
35062
35316
|
};
|
|
35063
35317
|
}
|
|
35318
|
+
if (cached && now >= expireAt) this.renderCache.delete(cacheKey);
|
|
35064
35319
|
}
|
|
35065
35320
|
const tree = this.getRouteTree();
|
|
35066
35321
|
if (!tree) {
|
|
@@ -35229,20 +35484,26 @@ ${bodyHTML}`;
|
|
|
35229
35484
|
durationMs: performance.now() - startTime,
|
|
35230
35485
|
renderMode: this.extractRenderMode(ctx.page)
|
|
35231
35486
|
};
|
|
35232
|
-
const
|
|
35487
|
+
const pageCache = this.extractCacheConfig(ctx.page);
|
|
35233
35488
|
if (this.enableCache) {
|
|
35234
35489
|
if (this.renderCache.size >= 100) {
|
|
35235
35490
|
const oldest = this.renderCache.keys().next().value;
|
|
35236
35491
|
if (oldest) this.renderCache.delete(oldest);
|
|
35237
35492
|
}
|
|
35493
|
+
const freshMs = pageCache ? pageCache.revalidate * 1e3 : this.cacheTTL;
|
|
35494
|
+
const expireMs = pageCache ? pageCache.expire * 1e3 : this.cacheTTL;
|
|
35238
35495
|
this.renderCache.set(cacheKey, {
|
|
35239
35496
|
result,
|
|
35240
|
-
expiresAt: Date.now() +
|
|
35241
|
-
|
|
35497
|
+
expiresAt: Date.now() + freshMs,
|
|
35498
|
+
freshUntil: Date.now() + freshMs,
|
|
35499
|
+
expireAt: Date.now() + expireMs,
|
|
35500
|
+
swr: !!pageCache,
|
|
35501
|
+
cache: pageCache,
|
|
35502
|
+
createdAt: Date.now(),
|
|
35242
35503
|
pathname
|
|
35243
35504
|
});
|
|
35244
35505
|
}
|
|
35245
|
-
return { ...result, headers:
|
|
35506
|
+
return { ...result, headers: this.stampCacheHeaders(result.headers, this.renderCache.get(cacheKey), Date.now(), "MISS") };
|
|
35246
35507
|
} catch (err2) {
|
|
35247
35508
|
const globalError = findGlobalError(tree);
|
|
35248
35509
|
if (globalError) {
|
|
@@ -35413,6 +35674,45 @@ ${bodyHTML}`;
|
|
|
35413
35674
|
return "static";
|
|
35414
35675
|
}
|
|
35415
35676
|
}
|
|
35677
|
+
/**
|
|
35678
|
+
* Cache tags (docs/cache-tags.md): expire every render-cache entry
|
|
35679
|
+
* whose `cache { tag "..." }` family matches. The next request for those
|
|
35680
|
+
* routes renders fresh (MISS). Entries are the single source of truth;
|
|
35681
|
+
* scanning them is the derived tag index (rebuilt by construction).
|
|
35682
|
+
* Returns the number of dropped entries.
|
|
35683
|
+
*/
|
|
35684
|
+
revalidateTag(tag) {
|
|
35685
|
+
let dropped = 0;
|
|
35686
|
+
for (const [key, entry] of this.renderCache) {
|
|
35687
|
+
const c = entry.cache;
|
|
35688
|
+
if (c && c.tag === tag) {
|
|
35689
|
+
this.renderCache.delete(key);
|
|
35690
|
+
dropped++;
|
|
35691
|
+
}
|
|
35692
|
+
}
|
|
35693
|
+
return dropped;
|
|
35694
|
+
}
|
|
35695
|
+
/**
|
|
35696
|
+
* Cache headers on every cacheable response (docs/cache-tags.md):
|
|
35697
|
+
* x-tw-cache: HIT | STALE | MISS (existing contract, unchanged)
|
|
35698
|
+
* x-tw-cache-age: seconds since the entry was rendered (observation)
|
|
35699
|
+
* Cache-Control: public, max-age=<stale>, stale-while-revalidate=...
|
|
35700
|
+
* only when `stale > 0` -- the client-side hint. The server
|
|
35701
|
+
* render-cache never reads `stale`.
|
|
35702
|
+
*/
|
|
35703
|
+
stampCacheHeaders(headers, entry, now, verdict) {
|
|
35704
|
+
const out = { ...headers ?? {}, "x-tw-cache": verdict };
|
|
35705
|
+
const c = entry?.cache;
|
|
35706
|
+
if (c) {
|
|
35707
|
+
const ageSec = entry?.createdAt ? Math.max(0, Math.floor((now - entry.createdAt) / 1e3)) : 0;
|
|
35708
|
+
out["x-tw-cache-age"] = String(ageSec);
|
|
35709
|
+
if (c.stale > 0) {
|
|
35710
|
+
const swr = Number.isFinite(c.expire) ? ", stale-while-revalidate=" + Math.max(0, c.expire - c.stale) : "";
|
|
35711
|
+
out["Cache-Control"] = "public, max-age=" + c.stale + swr;
|
|
35712
|
+
}
|
|
35713
|
+
}
|
|
35714
|
+
return out;
|
|
35715
|
+
}
|
|
35416
35716
|
extractRevalidate(file) {
|
|
35417
35717
|
try {
|
|
35418
35718
|
const source = (0, import_node_fs6.readFileSync)(file.absolutePath, "utf-8");
|
|
@@ -35426,6 +35726,34 @@ ${bodyHTML}`;
|
|
|
35426
35726
|
return null;
|
|
35427
35727
|
}
|
|
35428
35728
|
}
|
|
35729
|
+
/**
|
|
35730
|
+
* Cache config (docs/cache-tags.md): resolve a page's `cache { }`
|
|
35731
|
+
* directive against tw.config.ts cache.profiles, falling back to the
|
|
35732
|
+
* legacy `revalidate N` ISR form. The legacy form desugars to
|
|
35733
|
+
* { revalidate: N, stale: 0, expire: Infinity } -- the exact v1.0.5
|
|
35734
|
+
* behavior (fresh for N seconds, then stale-while-revalidate forever).
|
|
35735
|
+
*/
|
|
35736
|
+
extractCacheConfig(file) {
|
|
35737
|
+
try {
|
|
35738
|
+
const source = (0, import_node_fs6.readFileSync)(file.absolutePath, "utf-8");
|
|
35739
|
+
const meta = extractCacheDirective(source);
|
|
35740
|
+
if (meta) {
|
|
35741
|
+
try {
|
|
35742
|
+
return resolveCache(meta, getCacheProfiles(this.rootDir));
|
|
35743
|
+
} catch (e) {
|
|
35744
|
+
console.error("[tw] " + e.message + " (" + file.absolutePath + ")");
|
|
35745
|
+
return null;
|
|
35746
|
+
}
|
|
35747
|
+
}
|
|
35748
|
+
const legacy = this.extractRevalidate(file);
|
|
35749
|
+
if (legacy != null) {
|
|
35750
|
+
return { revalidate: legacy, stale: 0, expire: Infinity };
|
|
35751
|
+
}
|
|
35752
|
+
return null;
|
|
35753
|
+
} catch {
|
|
35754
|
+
return null;
|
|
35755
|
+
}
|
|
35756
|
+
}
|
|
35429
35757
|
/**
|
|
35430
35758
|
* Metadata API (docs/metadata.md): description / keywords / og_title /
|
|
35431
35759
|
* og_description / og_image in the page frontmatter become <meta> tags --
|
|
@@ -37161,8 +37489,8 @@ var init_static = __esm({
|
|
|
37161
37489
|
"../../packages/server/tw/static/index.ts"() {
|
|
37162
37490
|
import_node_fs8 = require("node:fs");
|
|
37163
37491
|
import_node_path9 = require("node:path");
|
|
37164
|
-
|
|
37165
|
-
|
|
37492
|
+
init_tw();
|
|
37493
|
+
init_tw();
|
|
37166
37494
|
StaticHandler = class {
|
|
37167
37495
|
root;
|
|
37168
37496
|
maxAge;
|
|
@@ -46119,7 +46447,7 @@ var import_node_fs9, import_node_crypto5, import_node_path10, import_node_fs10,
|
|
|
46119
46447
|
var init_ssr = __esm({
|
|
46120
46448
|
"../../packages/server/tw/ssr/index.ts"() {
|
|
46121
46449
|
import_node_fs9 = require("node:fs");
|
|
46122
|
-
|
|
46450
|
+
init_tw();
|
|
46123
46451
|
import_node_crypto5 = require("node:crypto");
|
|
46124
46452
|
import_node_path10 = require("node:path");
|
|
46125
46453
|
import_node_fs10 = require("node:fs");
|
|
@@ -47136,7 +47464,7 @@ function applyRewrites(rewrites, pathname) {
|
|
|
47136
47464
|
var import_node_path11, import_node_fs11, TWServer;
|
|
47137
47465
|
var init_tw6 = __esm({
|
|
47138
47466
|
"../../packages/server/tw/index.ts"() {
|
|
47139
|
-
|
|
47467
|
+
init_tw2();
|
|
47140
47468
|
init_websocket_manager();
|
|
47141
47469
|
init_routing2();
|
|
47142
47470
|
import_node_path11 = require("node:path");
|
|
@@ -47162,7 +47490,9 @@ var init_tw6 = __esm({
|
|
|
47162
47490
|
router;
|
|
47163
47491
|
pipeline;
|
|
47164
47492
|
staticHandler = null;
|
|
47165
|
-
/** ISR routes (from .tw/routes.json): pathname -> revalidate seconds
|
|
47493
|
+
/** ISR routes (from .tw/routes.json): pathname -> revalidate seconds
|
|
47494
|
+
* (bare number, legacy) or { revalidate, stale, expire, tag } object
|
|
47495
|
+
* (docs/cache-tags.md). Presence decides pipeline-vs-static routing. */
|
|
47166
47496
|
isrRoutes = /* @__PURE__ */ new Map();
|
|
47167
47497
|
securityHeaders = null;
|
|
47168
47498
|
/**
|
|
@@ -47265,7 +47595,7 @@ var init_tw6 = __esm({
|
|
|
47265
47595
|
if ((0, import_node_fs11.existsSync)(manifestPath)) {
|
|
47266
47596
|
const manifest = JSON.parse((0, import_node_fs11.readFileSync)(manifestPath, "utf-8"));
|
|
47267
47597
|
for (const [route, secs] of Object.entries(manifest)) {
|
|
47268
|
-
this.isrRoutes.set(route,
|
|
47598
|
+
this.isrRoutes.set(route, secs);
|
|
47269
47599
|
}
|
|
47270
47600
|
if (Object.keys(manifest).length > 0) {
|
|
47271
47601
|
console.log(` ISR: ${Object.keys(manifest).length} revalidate route(s)`);
|
|
@@ -47881,11 +48211,84 @@ __export(twm_loader_exports, {
|
|
|
47881
48211
|
executeRouteHandler: () => executeRouteHandler,
|
|
47882
48212
|
loadTWMModule: () => loadTWMModule,
|
|
47883
48213
|
revalidateRoute: () => revalidateRoute,
|
|
47884
|
-
shouldMatchMiddleware: () => shouldMatchMiddleware
|
|
48214
|
+
shouldMatchMiddleware: () => shouldMatchMiddleware,
|
|
48215
|
+
stripCommentsSafe: () => stripCommentsSafe
|
|
47885
48216
|
});
|
|
47886
48217
|
function clearTWMCache() {
|
|
47887
48218
|
moduleCache.clear();
|
|
47888
48219
|
}
|
|
48220
|
+
function stripCommentsSafe(src) {
|
|
48221
|
+
let out = "";
|
|
48222
|
+
let i = 0;
|
|
48223
|
+
let mode = "code";
|
|
48224
|
+
while (i < src.length) {
|
|
48225
|
+
const c = src[i];
|
|
48226
|
+
const n = i + 1 < src.length ? src[i + 1] : "";
|
|
48227
|
+
if (mode === "code") {
|
|
48228
|
+
if (c === "/" && n === "/") {
|
|
48229
|
+
mode = "line";
|
|
48230
|
+
i += 2;
|
|
48231
|
+
continue;
|
|
48232
|
+
}
|
|
48233
|
+
if (c === "/" && n === "*") {
|
|
48234
|
+
mode = "block";
|
|
48235
|
+
i += 2;
|
|
48236
|
+
continue;
|
|
48237
|
+
}
|
|
48238
|
+
if (c === "'") {
|
|
48239
|
+
mode = "sq";
|
|
48240
|
+
out += c;
|
|
48241
|
+
i++;
|
|
48242
|
+
continue;
|
|
48243
|
+
}
|
|
48244
|
+
if (c === '"') {
|
|
48245
|
+
mode = "dq";
|
|
48246
|
+
out += c;
|
|
48247
|
+
i++;
|
|
48248
|
+
continue;
|
|
48249
|
+
}
|
|
48250
|
+
if (c === "`") {
|
|
48251
|
+
mode = "tpl";
|
|
48252
|
+
out += c;
|
|
48253
|
+
i++;
|
|
48254
|
+
continue;
|
|
48255
|
+
}
|
|
48256
|
+
out += c;
|
|
48257
|
+
i++;
|
|
48258
|
+
continue;
|
|
48259
|
+
}
|
|
48260
|
+
if (mode === "line") {
|
|
48261
|
+
if (c === "\n") {
|
|
48262
|
+
mode = "code";
|
|
48263
|
+
out += c;
|
|
48264
|
+
}
|
|
48265
|
+
i++;
|
|
48266
|
+
continue;
|
|
48267
|
+
}
|
|
48268
|
+
if (mode === "block") {
|
|
48269
|
+
if (c === "*" && n === "/") {
|
|
48270
|
+
mode = "code";
|
|
48271
|
+
out += " ";
|
|
48272
|
+
i += 2;
|
|
48273
|
+
continue;
|
|
48274
|
+
}
|
|
48275
|
+
i++;
|
|
48276
|
+
continue;
|
|
48277
|
+
}
|
|
48278
|
+
out += c;
|
|
48279
|
+
if (c === "\\") {
|
|
48280
|
+
if (n) {
|
|
48281
|
+
out += n;
|
|
48282
|
+
i += 2;
|
|
48283
|
+
continue;
|
|
48284
|
+
}
|
|
48285
|
+
} else if (mode === "sq" && c === "'" || mode === "dq" && c === '"' || mode === "tpl" && c === "`") {
|
|
48286
|
+
mode = "code";
|
|
48287
|
+
}
|
|
48288
|
+
i++;
|
|
48289
|
+
}
|
|
48290
|
+
return out;
|
|
48291
|
+
}
|
|
47889
48292
|
async function loadLibModule(rootDir, name) {
|
|
47890
48293
|
let rel = name.replace(/^@\//, "").replace(/^\.\//, "").replace(/^lib\//, "");
|
|
47891
48294
|
const cacheKey = rootDir + "/" + rel;
|
|
@@ -47904,13 +48307,13 @@ async function loadLibModule(rootDir, name) {
|
|
|
47904
48307
|
}
|
|
47905
48308
|
}
|
|
47906
48309
|
function compileTwm(source) {
|
|
47907
|
-
const cleaned = source
|
|
48310
|
+
const cleaned = stripCommentsSafe(source);
|
|
47908
48311
|
const importRegex = /import\s+\{([^}]+)\}\s+from\s+["']([^"']+)["'];?/g;
|
|
47909
48312
|
const codeWithoutImports = cleaned.replace(importRegex, "");
|
|
47910
|
-
return codeWithoutImports.replace(/\bexport\s+(?=(async\s+)?(function|const|let|var|class))/g, "").replace(/\bfn\s+delete\b/g, "async function deleteFn").replace(/\bfn\s+/g, "async function ");
|
|
48313
|
+
return codeWithoutImports.replace(/\bexport\s+(?=(async\s+)?(function|const|let|var|class))/g, "").replace(/^[ \t]*cache\s*\{[^}]*\}[ \t]*;?[ \t]*$/gm, "").replace(/\bfn\s+cached\s+delete\b/g, "async function deleteFn").replace(/\bfn\s+cached\s+/g, "async function ").replace(/\bfn\s+delete\b/g, "async function deleteFn").replace(/\bfn\s+/g, "async function ");
|
|
47911
48314
|
}
|
|
47912
48315
|
function extractImports(source) {
|
|
47913
|
-
const cleaned = source
|
|
48316
|
+
const cleaned = stripCommentsSafe(source);
|
|
47914
48317
|
const importRegex = /import\s+\{([^}]+)\}\s+from\s+["']([^"']+)["'];?/g;
|
|
47915
48318
|
const imports = [];
|
|
47916
48319
|
let match;
|
|
@@ -47957,6 +48360,7 @@ async function loadTWMModule(filePath, rootDir) {
|
|
|
47957
48360
|
for (const name of imp.names) {
|
|
47958
48361
|
if (name === "revalidatePath") injected.revalidatePath = revalidatePath;
|
|
47959
48362
|
else if (name === "revalidateRoute") injected.revalidateRoute = revalidateRoute;
|
|
48363
|
+
else if (name === "revalidateTag") injected.revalidateTag = revalidateTag;
|
|
47960
48364
|
else if (name === "setSignal") injected.setSignal = setSignal;
|
|
47961
48365
|
}
|
|
47962
48366
|
try {
|
|
@@ -48015,6 +48419,41 @@ async function loadTWMModule(filePath, rootDir) {
|
|
|
48015
48419
|
const actionNames = /* @__PURE__ */ new Set();
|
|
48016
48420
|
for (const m of codeWithoutImports.matchAll(/\bfunction\s+(action[A-Za-z0-9_]*)\s*\(/g)) actionNames.add(m[1]);
|
|
48017
48421
|
for (const m of codeWithoutImports.matchAll(/\b(?:const|let|var)\s+(action[A-Za-z0-9_]*)\s*=/g)) actionNames.add(m[1]);
|
|
48422
|
+
const cachedHandlers = {};
|
|
48423
|
+
const cleanedSource = stripCommentsSafe(source);
|
|
48424
|
+
const fnCachedRegex = /\bfn\s+cached\s+([A-Za-z0-9_]+)\s*\(([^)]*)\)\s*\{/g;
|
|
48425
|
+
for (const cm of cleanedSource.matchAll(fnCachedRegex)) {
|
|
48426
|
+
const fnName = cm[1] === "delete" ? "deleteFn" : cm[1];
|
|
48427
|
+
const after = cleanedSource.slice((cm.index ?? 0) + cm[0].length);
|
|
48428
|
+
const bodyEnd = handlerBodyEnd(after);
|
|
48429
|
+
const body = after.slice(0, bodyEnd);
|
|
48430
|
+
const cacheBlock = /^\s*cache\s*\{([^}]*)\}/.exec(after);
|
|
48431
|
+
const meta = cacheBlock ? parseCacheBody(cacheBlock[1]) : {};
|
|
48432
|
+
if (meta.revalidate == null && !meta.life) {
|
|
48433
|
+
console.error("[tw] TW090: fn cached " + cm[1] + " in " + filePath + ' needs cache { revalidate N } or cache { life "profile" } -- not cached');
|
|
48434
|
+
continue;
|
|
48435
|
+
}
|
|
48436
|
+
let impure = null;
|
|
48437
|
+
for (const [pattern, label] of IMPURE_PATTERNS) {
|
|
48438
|
+
if (pattern.test(body)) {
|
|
48439
|
+
impure = label;
|
|
48440
|
+
break;
|
|
48441
|
+
}
|
|
48442
|
+
}
|
|
48443
|
+
if (impure) {
|
|
48444
|
+
console.error("[tw] TW091: fn cached " + cm[1] + " in " + filePath + " is impure (" + impure + ") -- handler excluded from caching");
|
|
48445
|
+
continue;
|
|
48446
|
+
}
|
|
48447
|
+
if (NONDET_PATTERN.test(body)) {
|
|
48448
|
+
console.warn("[tw] TW093: fn cached " + cm[1] + " in " + filePath + " calls a non-deterministic function -- the value freezes into the cache entry");
|
|
48449
|
+
}
|
|
48450
|
+
try {
|
|
48451
|
+
const resolved = resolveCache(meta, twmCacheProfiles(rd));
|
|
48452
|
+
if (resolved) cachedHandlers[fnName] = resolved;
|
|
48453
|
+
} catch (e) {
|
|
48454
|
+
console.error("[tw] " + e.message + " (" + filePath + ":" + cm[1] + ")");
|
|
48455
|
+
}
|
|
48456
|
+
}
|
|
48018
48457
|
const moduleCode = codeWithoutImports + "\nreturn { get: typeof get !== 'undefined' ? get : null, post: typeof post !== 'undefined' ? post : null, put: typeof put !== 'undefined' ? put : null, patch: typeof patch !== 'undefined' ? patch : null, deleteFn: typeof deleteFn !== 'undefined' ? deleteFn : null, GET: typeof GET !== 'undefined' ? GET : null, POST: typeof POST !== 'undefined' ? POST : null, PUT: typeof PUT !== 'undefined' ? PUT : null, PATCH: typeof PATCH !== 'undefined' ? PATCH : null, DELETE: typeof DELETE !== 'undefined' ? DELETE : null, middleware: typeof middleware !== 'undefined' ? middleware : null, generate: typeof generate !== 'undefined' ? generate : null, config: typeof config !== 'undefined' ? config : null, action: typeof action !== 'undefined' ? action : null, revalidate: typeof revalidate !== 'undefined' ? revalidate : null, " + [...actionNames].map((n) => `${n}: typeof ${n} !== 'undefined' ? ${n} : null`).join(", ") + " };";
|
|
48019
48458
|
const injectedKeys = Object.keys(injected);
|
|
48020
48459
|
let module2 = {};
|
|
@@ -48040,6 +48479,7 @@ async function loadTWMModule(filePath, rootDir) {
|
|
|
48040
48479
|
} catch (err2) {
|
|
48041
48480
|
console.error("[twm-loader] Error parsing " + filePath + ":", err2);
|
|
48042
48481
|
}
|
|
48482
|
+
module2.__twCached = cachedHandlers;
|
|
48043
48483
|
moduleCache.set(cacheKey, module2);
|
|
48044
48484
|
return module2;
|
|
48045
48485
|
}
|
|
@@ -48074,6 +48514,39 @@ function sameOriginAllowed(req) {
|
|
|
48074
48514
|
}
|
|
48075
48515
|
function clearRouteCache() {
|
|
48076
48516
|
routeCache.clear();
|
|
48517
|
+
handlerCache.clear();
|
|
48518
|
+
}
|
|
48519
|
+
function handlerBodyEnd(after) {
|
|
48520
|
+
let depth = 1;
|
|
48521
|
+
let inStr = null;
|
|
48522
|
+
for (let i = 0; i < after.length; i++) {
|
|
48523
|
+
const ch = after[i];
|
|
48524
|
+
if (inStr) {
|
|
48525
|
+
if (ch === "\\") i++;
|
|
48526
|
+
else if (ch === inStr) inStr = null;
|
|
48527
|
+
continue;
|
|
48528
|
+
}
|
|
48529
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
48530
|
+
inStr = ch;
|
|
48531
|
+
continue;
|
|
48532
|
+
}
|
|
48533
|
+
if (ch === "{") depth++;
|
|
48534
|
+
else if (ch === "}") {
|
|
48535
|
+
depth--;
|
|
48536
|
+
if (depth === 0) return i;
|
|
48537
|
+
}
|
|
48538
|
+
}
|
|
48539
|
+
return after.length;
|
|
48540
|
+
}
|
|
48541
|
+
function twmCacheProfiles(rootDir) {
|
|
48542
|
+
if (__twmProfiles.has(rootDir)) return __twmProfiles.get(rootDir);
|
|
48543
|
+
let profiles = {};
|
|
48544
|
+
try {
|
|
48545
|
+
profiles = readCacheProfilesSync(rootDir);
|
|
48546
|
+
} catch {
|
|
48547
|
+
}
|
|
48548
|
+
__twmProfiles.set(rootDir, profiles);
|
|
48549
|
+
return profiles;
|
|
48077
48550
|
}
|
|
48078
48551
|
function revalidateRoute(pathname, opts) {
|
|
48079
48552
|
const want = pathname.startsWith("/") ? pathname : "/" + pathname;
|
|
@@ -48177,12 +48650,17 @@ async function executeRouteHandler(filePath, method, request, rootDir) {
|
|
|
48177
48650
|
if (!actionResult || typeof actionResult !== "object") {
|
|
48178
48651
|
return { status: 500, json: { ok: false, error: "Internal Server Error" } };
|
|
48179
48652
|
}
|
|
48653
|
+
let revalidatedTag;
|
|
48654
|
+
if (typeof actionResult.revalidateTag === "string" && actionResult.revalidateTag) {
|
|
48655
|
+
revalidatedTag = actionResult.revalidateTag;
|
|
48656
|
+
revalidateTag(revalidatedTag);
|
|
48657
|
+
}
|
|
48180
48658
|
return {
|
|
48181
48659
|
status: typeof actionResult.status === "number" ? actionResult.status : 200,
|
|
48182
48660
|
json: actionResult.json || actionResult.body || {},
|
|
48183
48661
|
html: typeof actionResult.html === "string" ? actionResult.html : void 0,
|
|
48184
48662
|
text: typeof actionResult.text === "string" ? actionResult.text : void 0,
|
|
48185
|
-
headers: actionResult.headers
|
|
48663
|
+
headers: revalidatedTag ? { ...actionResult.headers ?? {}, "x-tw-revalidated": revalidatedTag } : actionResult.headers
|
|
48186
48664
|
};
|
|
48187
48665
|
}
|
|
48188
48666
|
const rv = mod.revalidate;
|
|
@@ -48203,6 +48681,57 @@ async function executeRouteHandler(filePath, method, request, rootDir) {
|
|
|
48203
48681
|
};
|
|
48204
48682
|
}
|
|
48205
48683
|
}
|
|
48684
|
+
const upperMethod = method.toUpperCase();
|
|
48685
|
+
let handlerConf = null;
|
|
48686
|
+
let handlerCacheKey = null;
|
|
48687
|
+
if ((upperMethod === "GET" || upperMethod === "HEAD") && mod.__twCached) {
|
|
48688
|
+
const handlerKey = methodLower === "delete" ? "deleteFn" : methodLower === "head" ? "get" : methodLower.toLowerCase();
|
|
48689
|
+
handlerConf = mod.__twCached[handlerKey] ?? null;
|
|
48690
|
+
if (handlerConf) {
|
|
48691
|
+
try {
|
|
48692
|
+
const u = new URL(request.url ?? "http://local");
|
|
48693
|
+
handlerCacheKey = sha256(
|
|
48694
|
+
upperMethod + "\n" + u.pathname + "\n" + canonicalizeQuery(u.search) + "\n" + JSON.stringify(request?.params ?? {})
|
|
48695
|
+
);
|
|
48696
|
+
} catch {
|
|
48697
|
+
handlerCacheKey = null;
|
|
48698
|
+
}
|
|
48699
|
+
}
|
|
48700
|
+
if (handlerCacheKey) {
|
|
48701
|
+
const hEntry = handlerCache.get(handlerCacheKey);
|
|
48702
|
+
const hNow = Date.now();
|
|
48703
|
+
if (hEntry && hNow < hEntry.freshUntil) {
|
|
48704
|
+
return {
|
|
48705
|
+
...hEntry.result,
|
|
48706
|
+
headers: {
|
|
48707
|
+
...hEntry.result.headers ?? {},
|
|
48708
|
+
"x-tw-cache": "HIT",
|
|
48709
|
+
"x-tw-cache-age": String(Math.max(0, Math.floor((hNow - hEntry.createdAt) / 1e3)))
|
|
48710
|
+
}
|
|
48711
|
+
};
|
|
48712
|
+
}
|
|
48713
|
+
if (hEntry && hNow < hEntry.expireAt && !pendingHandlerRefresh.has(handlerCacheKey)) {
|
|
48714
|
+
pendingHandlerRefresh.add(handlerCacheKey);
|
|
48715
|
+
void (async () => {
|
|
48716
|
+
try {
|
|
48717
|
+
await executeRouteHandler(filePath, method, request, rootDir);
|
|
48718
|
+
} catch {
|
|
48719
|
+
} finally {
|
|
48720
|
+
pendingHandlerRefresh.delete(handlerCacheKey);
|
|
48721
|
+
}
|
|
48722
|
+
})();
|
|
48723
|
+
return {
|
|
48724
|
+
...hEntry.result,
|
|
48725
|
+
headers: {
|
|
48726
|
+
...hEntry.result.headers ?? {},
|
|
48727
|
+
"x-tw-cache": "STALE",
|
|
48728
|
+
"x-tw-cache-age": String(Math.max(0, Math.floor((hNow - hEntry.createdAt) / 1e3)))
|
|
48729
|
+
}
|
|
48730
|
+
};
|
|
48731
|
+
}
|
|
48732
|
+
if (hEntry && hNow >= hEntry.expireAt) handlerCache.delete(handlerCacheKey);
|
|
48733
|
+
}
|
|
48734
|
+
}
|
|
48206
48735
|
let result;
|
|
48207
48736
|
try {
|
|
48208
48737
|
result = await handler(req);
|
|
@@ -48230,6 +48759,33 @@ async function executeRouteHandler(filePath, method, request, rootDir) {
|
|
|
48230
48759
|
}
|
|
48231
48760
|
routeCache.set(routeCacheKey, { result: finalResult, expiresAt: Date.now() + rv * 1e3, pathname: cachedPath });
|
|
48232
48761
|
}
|
|
48762
|
+
if (handlerCacheKey && handlerConf && finalResult.status === 200) {
|
|
48763
|
+
const now = Date.now();
|
|
48764
|
+
let cachedPath = "";
|
|
48765
|
+
try {
|
|
48766
|
+
cachedPath = new URL(request.url ?? "http://local").pathname;
|
|
48767
|
+
} catch {
|
|
48768
|
+
cachedPath = "";
|
|
48769
|
+
}
|
|
48770
|
+
handlerCache.set(handlerCacheKey, {
|
|
48771
|
+
result: finalResult,
|
|
48772
|
+
freshUntil: now + handlerConf.revalidate * 1e3,
|
|
48773
|
+
expireAt: now + handlerConf.expire * 1e3,
|
|
48774
|
+
conf: handlerConf,
|
|
48775
|
+
createdAt: now,
|
|
48776
|
+
pathname: cachedPath
|
|
48777
|
+
});
|
|
48778
|
+
const cc = handlerConf.stale > 0 ? "public, max-age=" + handlerConf.stale + (Number.isFinite(handlerConf.expire) ? ", stale-while-revalidate=" + Math.max(0, handlerConf.expire - handlerConf.stale) : "") : null;
|
|
48779
|
+
return {
|
|
48780
|
+
...finalResult,
|
|
48781
|
+
headers: {
|
|
48782
|
+
...finalResult.headers ?? {},
|
|
48783
|
+
"x-tw-cache": "MISS",
|
|
48784
|
+
"x-tw-cache-age": "0",
|
|
48785
|
+
...cc ? { "Cache-Control": cc } : {}
|
|
48786
|
+
}
|
|
48787
|
+
};
|
|
48788
|
+
}
|
|
48233
48789
|
return finalResult;
|
|
48234
48790
|
}
|
|
48235
48791
|
async function executeMiddleware(filePath, request, rootDir) {
|
|
@@ -48268,7 +48824,7 @@ function shouldMatchMiddleware(matchers, pathname) {
|
|
|
48268
48824
|
}
|
|
48269
48825
|
return false;
|
|
48270
48826
|
}
|
|
48271
|
-
var import_node_fs12, import_node_path12, moduleCache, libCache, routeCache;
|
|
48827
|
+
var import_node_fs12, import_node_path12, moduleCache, libCache, routeCache, IMPURE_PATTERNS, NONDET_PATTERN, __twmProfiles, handlerCache, pendingHandlerRefresh;
|
|
48272
48828
|
var init_twm_loader = __esm({
|
|
48273
48829
|
"../../packages/server/tw/routing/twm-loader.ts"() {
|
|
48274
48830
|
import_node_fs12 = require("node:fs");
|
|
@@ -48276,9 +48832,30 @@ var init_twm_loader = __esm({
|
|
|
48276
48832
|
init_signal_stream();
|
|
48277
48833
|
import_node_path12 = require("node:path");
|
|
48278
48834
|
init_twm_rules();
|
|
48835
|
+
init_tw();
|
|
48279
48836
|
moduleCache = /* @__PURE__ */ new Map();
|
|
48280
48837
|
libCache = /* @__PURE__ */ new Map();
|
|
48281
48838
|
routeCache = /* @__PURE__ */ new Map();
|
|
48839
|
+
IMPURE_PATTERNS = [
|
|
48840
|
+
[/request\s*\.\s*cookies/, "request.cookies"],
|
|
48841
|
+
[/request\s*\.\s*headers/, "request.headers"],
|
|
48842
|
+
[/request\s*\.\s*body/, "request.body"],
|
|
48843
|
+
[/\bsetSignal\s*\(/, "setSignal(...)"]
|
|
48844
|
+
];
|
|
48845
|
+
NONDET_PATTERN = /\b(?:Date\s*\.\s*now\s*\(|Math\s*\.\s*random\s*\(|crypto\s*\.\s*randomUUID\s*\()/;
|
|
48846
|
+
__twmProfiles = /* @__PURE__ */ new Map();
|
|
48847
|
+
handlerCache = /* @__PURE__ */ new Map();
|
|
48848
|
+
pendingHandlerRefresh = /* @__PURE__ */ new Set();
|
|
48849
|
+
registerTwmTagInvalidator(function(tag) {
|
|
48850
|
+
let dropped = 0;
|
|
48851
|
+
for (const [key, entry] of handlerCache) {
|
|
48852
|
+
if (entry.conf && entry.conf.tag === tag) {
|
|
48853
|
+
handlerCache.delete(key);
|
|
48854
|
+
dropped++;
|
|
48855
|
+
}
|
|
48856
|
+
}
|
|
48857
|
+
return dropped;
|
|
48858
|
+
});
|
|
48282
48859
|
}
|
|
48283
48860
|
});
|
|
48284
48861
|
|