vxrn 1.17.0 → 1.17.1
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/exports/serveStaticAssets.mjs +67 -2
- package/dist/exports/serveStaticAssets.mjs.map +1 -1
- package/dist/exports/serveStaticAssets.native.js +106 -2
- package/dist/exports/serveStaticAssets.native.js.map +1 -1
- package/package.json +11 -11
- package/src/exports/serveStaticAssets.ts +115 -4
- package/types/exports/serveStaticAssets.d.ts.map +1 -1
|
@@ -1,7 +1,55 @@
|
|
|
1
1
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
2
|
+
import { statSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { getMimeType } from "hono/utils/mime";
|
|
2
5
|
import micromatch from "micromatch";
|
|
3
6
|
const assetsPathRe = /[\\/]assets[\\/]/;
|
|
4
7
|
const hashedAssetRe = /[.-](?=[a-zA-Z0-9_-]*\d)[a-zA-Z0-9_-]{8,}\.\w+$/;
|
|
8
|
+
const precompressedSuffixRe = /\.(?:br|gz|zst)$/;
|
|
9
|
+
const PRECOMPRESSED_ENCODINGS = [{
|
|
10
|
+
encoding: "br",
|
|
11
|
+
ext: ".br"
|
|
12
|
+
}, {
|
|
13
|
+
encoding: "zstd",
|
|
14
|
+
ext: ".zst"
|
|
15
|
+
}, {
|
|
16
|
+
encoding: "gzip",
|
|
17
|
+
ext: ".gz"
|
|
18
|
+
}];
|
|
19
|
+
const compressibleMimeRe = /^\s*(?:text\/[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|wasm|x-javascript|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/svg\+xml|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i;
|
|
20
|
+
function tryStat(path) {
|
|
21
|
+
try {
|
|
22
|
+
return statSync(path);
|
|
23
|
+
} catch {
|
|
24
|
+
return void 0;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function parseAcceptEncoding(header) {
|
|
28
|
+
if (!header) return /* @__PURE__ */new Set();
|
|
29
|
+
const out = /* @__PURE__ */new Set();
|
|
30
|
+
for (const part of header.split(",")) {
|
|
31
|
+
const enc = part.split(";")[0]?.trim().toLowerCase();
|
|
32
|
+
if (enc) out.add(enc);
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
function pickFreshSibling(sourceFsPath, acceptEncoding) {
|
|
37
|
+
const accepted = parseAcceptEncoding(acceptEncoding);
|
|
38
|
+
if (accepted.size === 0) return void 0;
|
|
39
|
+
const sourceStat = tryStat(sourceFsPath);
|
|
40
|
+
if (!sourceStat || !sourceStat.isFile()) return void 0;
|
|
41
|
+
const mime = getMimeType(sourceFsPath);
|
|
42
|
+
if (mime && !compressibleMimeRe.test(mime)) return void 0;
|
|
43
|
+
for (const candidate of PRECOMPRESSED_ENCODINGS) {
|
|
44
|
+
if (!accepted.has(candidate.encoding)) continue;
|
|
45
|
+
const siblingStat = tryStat(sourceFsPath + candidate.ext);
|
|
46
|
+
if (!siblingStat || !siblingStat.isFile()) continue;
|
|
47
|
+
if (siblingStat.mtimeMs >= sourceStat.mtimeMs) {
|
|
48
|
+
return candidate;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return void 0;
|
|
52
|
+
}
|
|
5
53
|
function compileCacheRules(cacheControl) {
|
|
6
54
|
const groups = /* @__PURE__ */new Map();
|
|
7
55
|
for (const [pattern, value] of Object.entries(cacheControl)) {
|
|
@@ -34,10 +82,27 @@ async function serveStaticAssets({
|
|
|
34
82
|
let didCallNext = false;
|
|
35
83
|
const root = `./${outDir}/client`;
|
|
36
84
|
const rootPrefix = `${outDir}/client/`;
|
|
85
|
+
const reqPath = context.req.path.replace(/^\/+/, "");
|
|
86
|
+
const sibling = pickFreshSibling(join(root, reqPath), context.req.header("Accept-Encoding"));
|
|
87
|
+
const originalMime = sibling ? getMimeType(reqPath) : void 0;
|
|
37
88
|
const response = await serveStatic({
|
|
38
89
|
root,
|
|
90
|
+
// when a fresh sibling exists, pin the served file to <path>.<ext>.
|
|
91
|
+
// hono will stat that path and stream it as-is; we set the right
|
|
92
|
+
// Content-Encoding + Content-Type in onFound.
|
|
93
|
+
path: sibling ? reqPath + sibling.ext : void 0,
|
|
39
94
|
onFound: (fsPath, c) => {
|
|
40
|
-
const
|
|
95
|
+
const originalFsPath = fsPath.replace(precompressedSuffixRe, "");
|
|
96
|
+
if (sibling) {
|
|
97
|
+
c.header("Content-Encoding", sibling.encoding);
|
|
98
|
+
c.header("Vary", "Accept-Encoding", {
|
|
99
|
+
append: true
|
|
100
|
+
});
|
|
101
|
+
if (originalMime) {
|
|
102
|
+
c.header("Content-Type", originalMime);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const path = originalFsPath.startsWith(rootPrefix) ? originalFsPath.slice(rootPrefix.length) : originalFsPath;
|
|
41
106
|
if (cacheRules) {
|
|
42
107
|
const m = cacheRules.re.exec(path);
|
|
43
108
|
if (m) {
|
|
@@ -49,7 +114,7 @@ async function serveStaticAssets({
|
|
|
49
114
|
}
|
|
50
115
|
}
|
|
51
116
|
}
|
|
52
|
-
if (assetsPathRe.test(
|
|
117
|
+
if (assetsPathRe.test(originalFsPath) || hashedAssetRe.test(originalFsPath)) {
|
|
53
118
|
c.header("Cache-Control", "public, immutable, max-age=31536000");
|
|
54
119
|
} else {
|
|
55
120
|
c.header("Cache-Control", "public, max-age=0, must-revalidate");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["serveStatic","micromatch","assetsPathRe","hashedAssetRe","compileCacheRules","cacheControl","groups","Map","pattern","value","Object","entries","arr","get","set","push","values","groupSources","patterns","sources","map","p","makeRe","source","
|
|
1
|
+
{"version":3,"names":["serveStatic","statSync","join","getMimeType","micromatch","assetsPathRe","hashedAssetRe","precompressedSuffixRe","PRECOMPRESSED_ENCODINGS","encoding","ext","compressibleMimeRe","tryStat","path","parseAcceptEncoding","header","Set","out","part","split","enc","trim","toLowerCase","add","pickFreshSibling","sourceFsPath","acceptEncoding","accepted","size","sourceStat","isFile","mime","test","candidate","has","siblingStat","mtimeMs","compileCacheRules","cacheControl","groups","Map","pattern","value","Object","entries","arr","get","set","push","values","groupSources","patterns","sources","map","p","makeRe","source","re","RegExp","serveStaticAssets","context","next","outDir","cacheRules","didCallNext","root","rootPrefix","reqPath","req","replace","sibling","originalMime","response","onFound","fsPath","c","originalFsPath","append","startsWith","slice","length","m","exec","i"],"sources":["../../src/exports/serveStaticAssets.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -1,7 +1,94 @@
|
|
|
1
1
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
2
|
+
import { statSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { getMimeType } from "hono/utils/mime";
|
|
2
5
|
import micromatch from "micromatch";
|
|
3
6
|
var assetsPathRe = /[\\/]assets[\\/]/;
|
|
4
7
|
var hashedAssetRe = /[.-](?=[a-zA-Z0-9_-]*\d)[a-zA-Z0-9_-]{8,}\.\w+$/;
|
|
8
|
+
var precompressedSuffixRe = /\.(?:br|gz|zst)$/;
|
|
9
|
+
var PRECOMPRESSED_ENCODINGS = [{
|
|
10
|
+
encoding: "br",
|
|
11
|
+
ext: ".br"
|
|
12
|
+
}, {
|
|
13
|
+
encoding: "zstd",
|
|
14
|
+
ext: ".zst"
|
|
15
|
+
}, {
|
|
16
|
+
encoding: "gzip",
|
|
17
|
+
ext: ".gz"
|
|
18
|
+
}];
|
|
19
|
+
var compressibleMimeRe = /^\s*(?:text\/[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|wasm|x-javascript|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/svg\+xml|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i;
|
|
20
|
+
function tryStat(path) {
|
|
21
|
+
try {
|
|
22
|
+
return statSync(path);
|
|
23
|
+
} catch (unused) {
|
|
24
|
+
return void 0;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function parseAcceptEncoding(header) {
|
|
28
|
+
if (!header) return /* @__PURE__ */new Set();
|
|
29
|
+
var out = /* @__PURE__ */new Set();
|
|
30
|
+
var _iteratorNormalCompletion = true,
|
|
31
|
+
_didIteratorError = false,
|
|
32
|
+
_iteratorError = void 0;
|
|
33
|
+
try {
|
|
34
|
+
for (var _iterator = header.split(",")[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
35
|
+
var part = _step.value;
|
|
36
|
+
var _part_split_;
|
|
37
|
+
var enc = (_part_split_ = part.split(";")[0]) === null || _part_split_ === void 0 ? void 0 : _part_split_.trim().toLowerCase();
|
|
38
|
+
if (enc) out.add(enc);
|
|
39
|
+
}
|
|
40
|
+
} catch (err) {
|
|
41
|
+
_didIteratorError = true;
|
|
42
|
+
_iteratorError = err;
|
|
43
|
+
} finally {
|
|
44
|
+
try {
|
|
45
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
46
|
+
_iterator.return();
|
|
47
|
+
}
|
|
48
|
+
} finally {
|
|
49
|
+
if (_didIteratorError) {
|
|
50
|
+
throw _iteratorError;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
function pickFreshSibling(sourceFsPath, acceptEncoding) {
|
|
57
|
+
var accepted = parseAcceptEncoding(acceptEncoding);
|
|
58
|
+
if (accepted.size === 0) return void 0;
|
|
59
|
+
var sourceStat = tryStat(sourceFsPath);
|
|
60
|
+
if (!sourceStat || !sourceStat.isFile()) return void 0;
|
|
61
|
+
var mime = getMimeType(sourceFsPath);
|
|
62
|
+
if (mime && !compressibleMimeRe.test(mime)) return void 0;
|
|
63
|
+
var _iteratorNormalCompletion = true,
|
|
64
|
+
_didIteratorError = false,
|
|
65
|
+
_iteratorError = void 0;
|
|
66
|
+
try {
|
|
67
|
+
for (var _iterator = PRECOMPRESSED_ENCODINGS[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
68
|
+
var candidate = _step.value;
|
|
69
|
+
if (!accepted.has(candidate.encoding)) continue;
|
|
70
|
+
var siblingStat = tryStat(sourceFsPath + candidate.ext);
|
|
71
|
+
if (!siblingStat || !siblingStat.isFile()) continue;
|
|
72
|
+
if (siblingStat.mtimeMs >= sourceStat.mtimeMs) {
|
|
73
|
+
return candidate;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
} catch (err) {
|
|
77
|
+
_didIteratorError = true;
|
|
78
|
+
_iteratorError = err;
|
|
79
|
+
} finally {
|
|
80
|
+
try {
|
|
81
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
82
|
+
_iterator.return();
|
|
83
|
+
}
|
|
84
|
+
} finally {
|
|
85
|
+
if (_didIteratorError) {
|
|
86
|
+
throw _iteratorError;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return void 0;
|
|
91
|
+
}
|
|
5
92
|
function compileCacheRules(cacheControl) {
|
|
6
93
|
var groups = /* @__PURE__ */new Map();
|
|
7
94
|
var _iteratorNormalCompletion = true,
|
|
@@ -75,10 +162,27 @@ async function serveStaticAssets(param) {
|
|
|
75
162
|
var didCallNext = false;
|
|
76
163
|
var root = `./${outDir}/client`;
|
|
77
164
|
var rootPrefix = `${outDir}/client/`;
|
|
165
|
+
var reqPath = context.req.path.replace(/^\/+/, "");
|
|
166
|
+
var sibling = pickFreshSibling(join(root, reqPath), context.req.header("Accept-Encoding"));
|
|
167
|
+
var originalMime = sibling ? getMimeType(reqPath) : void 0;
|
|
78
168
|
var response = await serveStatic({
|
|
79
169
|
root,
|
|
170
|
+
// when a fresh sibling exists, pin the served file to <path>.<ext>.
|
|
171
|
+
// hono will stat that path and stream it as-is; we set the right
|
|
172
|
+
// Content-Encoding + Content-Type in onFound.
|
|
173
|
+
path: sibling ? reqPath + sibling.ext : void 0,
|
|
80
174
|
onFound: function (fsPath, c) {
|
|
81
|
-
var
|
|
175
|
+
var originalFsPath = fsPath.replace(precompressedSuffixRe, "");
|
|
176
|
+
if (sibling) {
|
|
177
|
+
c.header("Content-Encoding", sibling.encoding);
|
|
178
|
+
c.header("Vary", "Accept-Encoding", {
|
|
179
|
+
append: true
|
|
180
|
+
});
|
|
181
|
+
if (originalMime) {
|
|
182
|
+
c.header("Content-Type", originalMime);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
var path = originalFsPath.startsWith(rootPrefix) ? originalFsPath.slice(rootPrefix.length) : originalFsPath;
|
|
82
186
|
if (cacheRules) {
|
|
83
187
|
var m = cacheRules.re.exec(path);
|
|
84
188
|
if (m) {
|
|
@@ -90,7 +194,7 @@ async function serveStaticAssets(param) {
|
|
|
90
194
|
}
|
|
91
195
|
}
|
|
92
196
|
}
|
|
93
|
-
if (assetsPathRe.test(
|
|
197
|
+
if (assetsPathRe.test(originalFsPath) || hashedAssetRe.test(originalFsPath)) {
|
|
94
198
|
c.header("Cache-Control", "public, immutable, max-age=31536000");
|
|
95
199
|
} else {
|
|
96
200
|
c.header("Cache-Control", "public, max-age=0, must-revalidate");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["serveStatic","micromatch","assetsPathRe","hashedAssetRe","
|
|
1
|
+
{"version":3,"names":["serveStatic","statSync","join","getMimeType","micromatch","assetsPathRe","hashedAssetRe","precompressedSuffixRe","PRECOMPRESSED_ENCODINGS","encoding","ext","compressibleMimeRe","tryStat","path","unused","parseAcceptEncoding","header","Set","out","_iteratorNormalCompletion","_didIteratorError","_iteratorError","_iterator","split","Symbol","iterator","_step","next","done","part","value","_part_split_","enc","trim","toLowerCase","add","err","return","pickFreshSibling","sourceFsPath","acceptEncoding","accepted","size","sourceStat","isFile","mime","test","candidate","has","siblingStat","mtimeMs","compileCacheRules","cacheControl","groups","Map","Object","entries","pattern","arr","get","set","push","values","groupSources","_iteratorNormalCompletion1","_didIteratorError1","_iteratorError1","_iterator1","_step1","value1","patterns"],"sources":["../../src/exports/serveStaticAssets.ts"],"sourcesContent":[null],"mappings":"AAAA,SAASA,WAAA,QAAmB;AAE5B,SAASC,QAAA,QAAgB;AACzB,SAASC,IAAA,QAAY;AACrB,SAASC,WAAA,QAAmB;AAC5B,OAAOC,UAAA,MAAgB;AAIvB,IAAAC,YAAM,qBAAe;AAErB,IAAAC,aAAM,oDAAgB;AAEtB,IAAAC,qBAAM,qBAAwB;AAO9B,IAAAC,uBAAM,IACJ;EACEC,QAAA,EAAU;EACVC,GAAA;AACJ,GAMA;EAIAD,QAAS,QAAQ;EACfC,GAAI;AACF,GACF;EACED,QAAO;EACTC,GAAA;AACF,EAIA;AACE,IAAAC,kBAAoB,yMAAQ;AAC5B,SAAMC,OAAMA,CAAAC,IAAA;EACZ;IACE,OAAMZ,QAAM,CAAAY,IAAK;EACjB,SAAIC,MAAS;IACf;EACA;AACF;AAYA,SAASC,mBACPA,CAAAC,MAAA;EAGA,KAAAA,MAAM,SAAW,mBAAoBC,GAAA;EACrC,IAAIC,GAAA,kBAAqB,IAAAD,GAAO;EAEhC,IAAAE,yBAA2B;IAAAC,iBAAY;IAAAC,cAAA;EACvC,IAAI;IAGJ,KAAM,IAAAC,SAAO,GAAAN,MAAY,CAAAO,KAAA,IAAY,EAAAC,MAAA,CAAAC,QAAA,KAAAC,KAAA,IAAAP,yBAAA,IAAAO,KAAA,GAAAJ,SAAA,CAAAK,IAAA,IAAAC,IAAA,GAAAT,yBAAA;MACjC,IAAAU,IAAQ,GAACH,KAAA,CAAAI,KAAA;MAEb,IAAAC,YAAW;MACT,IAAKC,GAAA,IAAAD,YAAa,GAAUF,IAAA,CAAAN,KAAW,sBAAAQ,YAAA,uBAAAA,YAAA,CAAAE,IAAA,GAAAC,WAAA;MACvC,IAAMF,GAAA,EAAAd,GAAA,CAAAiB,GAAA,CAAAH,GAAc;IACpB;EAGA,SAAII,GAAA;IACFhB,iBAAO;IACTC,cAAA,GAAAe,GAAA;EACF;IACA;MACF,KAAAjB,yBAAA,IAAAG,SAAA,CAAAe,MAAA;QASOf,SAAS,CAAAe,MAAA;MAId;IACA,UAAY;MACV,IAAIjB,iBAAiB;QACjB,MAAMC,cAAA;MACR;IACA;EAAqB;EAEvB,OAAIH,GAAA;AAAY;AAKlB,SAAMoB,gBAAoBA,CAAAC,YAAA,EAAAC,cAAA;EAC1B,IAAAC,QAAM,GAAA1B,mBAA0B,CAAAyB,cAAA;EAEhC,IAAAC,QAAY,CAAAC,IAAA,KAAO,UAAa;EAC9B,IAAAC,UAAY,GAAA/B,OAAK,CAAA2B,YAAA;EACjB,KAAAI,UAAM,IAAU,CAAAA,UAAS,CAAIC,MAAC,CAAM,eAAW;EAC/C,IAAAC,IAAA,GAAA1C,WAAkB,CAAAoC,YAAY;EAChC,IAAAM,IAAA,KAAAlC,kBAAA,CAAAmC,IAAA,CAAAD,IAAA;EAEA,IAAA1B,yBAAsB,OAAa;IAAAC,iBAAS;IAAAC,cAAA;EAC5C;IACF,SAAAC,SAAA,GAAAd,uBAAA,CAAAgB,MAAA,CAAAC,QAAA,KAAAC,KAAA,IAAAP,yBAAA,IAAAO,KAAA,GAAAJ,SAAA,CAAAK,IAAA,IAAAC,IAAA,GAAAT,yBAAA;MAEA,IAAA4B,SAAsB,GAAArB,KAAA,CAAAI,KAAkB;MACtC,KAAAW,QAAA,CAAAO,GAAA,CAAAD,SAAA,CAAAtC,QAAA;MACA,IAAAwC,WAAA,GAAArC,OAAA,CAAA2B,YAAA,GAAAQ,SAAA,CAAArC,GAAA;MACA,KAASuC,WAAA,KAAAA,WAAA,CAAAL,MAAA;MACT,IAAAK,WAAA,CAAAC,OAAA,IAAAP,UAAA,CAAAO,OAAA;QAMC,OAAAH,SAAA;MACG;IAEJ;EAEA,SAAMX,GAAA;IAINhB,iBAAgB,OAAQ;IACxBC,cAAgB,GAAAe,GAAA;EAAA,UACT;IACL;MACF,KAAAjB,yBAAA,IAAAG,SAAA,CAAAe,MAAA;QAIMf,SAAA,CAAAe,MAAe;MAErB;IACE;MAAA,IAAAjB,iBAAA;QAAA,MAAAC,cAAA;MAAA;IAIA;EAAwC;EAItC,YAAM;AAEN;AACE,SAAA8B,iBAASA,CAAAC,YAAoB;EAC7B,IAAAC,MAAE,kBAAe,IAAAC,GAAA;EAGjB,IAAAnC,yBAAkB;IAAAC,iBAAA;IAAAC,cAAA;EAChB;IAAqC,KACvC,IAAAC,SAAA,GAAAiC,MAAA,CAAAC,OAAA,CAAAJ,YAAA,EAAA5B,MAAA,CAAAC,QAAA,KAAAC,KAAA,IAAAP,yBAAA,IAAAO,KAAA,GAAAJ,SAAA,CAAAK,IAAA,IAAAC,IAAA,GAAAT,yBAAA;MACF,KAAAsC,OAAA,EAAA3B,KAAA,IAAAJ,KAAA,CAAAI,KAAA;MAIA,IAAA4B,GAAM,GAAAL,MAAO,CAAAM,GAAA,CAAA7B,KAAA;MAKb,IAAI,CAAA4B,GAAA;QACFA,GAAA,GAAM;QACNL,MAAI,CAAGO,GAAA,CAAA9B,KAAA,EAAA4B,GAAA;MAEL;MACEA,GAAA,CAAAG,IAAA,CAAAJ,OAAO;IACL;EACA,SAAArB,GAAA;IAAAhB,iBACF;IAAAC,cACF,GAAAe,GAAA;EAAA,UACF;IAAA,IACF;MAEA,IAAI,CAAAjB,yBAAkB,IAAAG,SAAmB,CAAAe,MAAA,QAAc,EAAK;QAC1Df,SAAS,CAAAe,MAAA;MACX;IACE,UAAE;MACJ,IAAAjB,iBAAA;QACF,MAAAC,cAAA;MACC;IACD;EACA;EACF,IAACyC,MAAA;EAED,IAAIC,YAAC,GAAY;EACf,IAAAC,0BAAA;IAAAC,kBAAA;IAAAC,eAAA;EACF;IAEA,KAAO,IAAAC,UAAA,GAAAd,MAAA,CAAA7B,MAAA,CAAAC,QAAA,KAAA2C,MAAA,IAAAJ,0BAAA,IAAAI,MAAA,GAAAD,UAAA,CAAAxC,IAAA,IAAAC,IAAA,GAAAoC,0BAAA;MACT,KAAAK,MAAA,EAAAC,QAAA,IAAAF,MAAA,CAAAtC,KAAA","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vxrn",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.1",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -59,16 +59,16 @@
|
|
|
59
59
|
"@expo/config-plugins": "~55.0.6",
|
|
60
60
|
"@hono/node-server": "^1.19.10",
|
|
61
61
|
"@react-native/dev-middleware": "^0.83.4",
|
|
62
|
-
"@vxrn/compiler": "1.17.
|
|
63
|
-
"@vxrn/debug": "1.17.
|
|
64
|
-
"@vxrn/query-string": "1.17.
|
|
65
|
-
"@vxrn/resolve": "1.17.
|
|
66
|
-
"@vxrn/safe-area": "1.17.
|
|
67
|
-
"@vxrn/url-parse": "1.17.
|
|
68
|
-
"@vxrn/utils": "1.17.
|
|
69
|
-
"@vxrn/vendor": "1.17.
|
|
70
|
-
"@vxrn/vite-flow": "1.17.
|
|
71
|
-
"@vxrn/vite-plugin-metro": "1.17.
|
|
62
|
+
"@vxrn/compiler": "1.17.1",
|
|
63
|
+
"@vxrn/debug": "1.17.1",
|
|
64
|
+
"@vxrn/query-string": "1.17.1",
|
|
65
|
+
"@vxrn/resolve": "1.17.1",
|
|
66
|
+
"@vxrn/safe-area": "1.17.1",
|
|
67
|
+
"@vxrn/url-parse": "1.17.1",
|
|
68
|
+
"@vxrn/utils": "1.17.1",
|
|
69
|
+
"@vxrn/vendor": "1.17.1",
|
|
70
|
+
"@vxrn/vite-flow": "1.17.1",
|
|
71
|
+
"@vxrn/vite-plugin-metro": "1.17.1",
|
|
72
72
|
"citty": "^0.1.6",
|
|
73
73
|
"dotenv": "^17.2.1",
|
|
74
74
|
"dotenv-expand": "^12.0.2",
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { serveStatic } from '@hono/node-server/serve-static'
|
|
2
2
|
import type { Context } from 'hono'
|
|
3
|
+
import { statSync } from 'node:fs'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
import { getMimeType } from 'hono/utils/mime'
|
|
3
6
|
import micromatch from 'micromatch'
|
|
4
7
|
|
|
5
8
|
// hashed assets can be cached forever, html must revalidate
|
|
@@ -7,6 +10,84 @@ import micromatch from 'micromatch'
|
|
|
7
10
|
const assetsPathRe = /[\\/]assets[\\/]/
|
|
8
11
|
// fallback regex for hashed filenames outside assets/
|
|
9
12
|
const hashedAssetRe = /[.-](?=[a-zA-Z0-9_-]*\d)[a-zA-Z0-9_-]{8,}\.\w+$/
|
|
13
|
+
// strip a content-encoding suffix from a path
|
|
14
|
+
const precompressedSuffixRe = /\.(?:br|gz|zst)$/
|
|
15
|
+
|
|
16
|
+
// preference order matches hono's built-in: br > zstd > gzip.
|
|
17
|
+
// we use this manually (not hono's `precompressed: true`) because hono
|
|
18
|
+
// does not mtime-guard the sibling — it'll happily serve a stale .br
|
|
19
|
+
// against a newer source. our build pipeline regenerates siblings, but
|
|
20
|
+
// the runtime guard is the safety net.
|
|
21
|
+
const PRECOMPRESSED_ENCODINGS: Array<{ encoding: string; ext: string }> = [
|
|
22
|
+
{ encoding: 'br', ext: '.br' },
|
|
23
|
+
{ encoding: 'zstd', ext: '.zst' },
|
|
24
|
+
{ encoding: 'gzip', ext: '.gz' },
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
// mime types eligible for precompressed sibling serving. mirrors hono's
|
|
28
|
+
// COMPRESSIBLE_CONTENT_TYPE_REGEX behavior — only types where brotli/gzip
|
|
29
|
+
// give meaningful compression. images, video, woff2 etc. are already
|
|
30
|
+
// compressed and skipped.
|
|
31
|
+
const compressibleMimeRe =
|
|
32
|
+
/^\s*(?:text\/[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|wasm|x-javascript|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/svg\+xml|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i
|
|
33
|
+
|
|
34
|
+
// safe stat that returns undefined on error
|
|
35
|
+
function tryStat(path: string) {
|
|
36
|
+
try {
|
|
37
|
+
return statSync(path)
|
|
38
|
+
} catch {
|
|
39
|
+
return undefined
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// parse Accept-Encoding into a set of lowercase encodings the client accepts.
|
|
44
|
+
// ignores q-values (no real client uses q=0 to reject an encoding in practice).
|
|
45
|
+
function parseAcceptEncoding(header: string | undefined): Set<string> {
|
|
46
|
+
if (!header) return new Set()
|
|
47
|
+
const out = new Set<string>()
|
|
48
|
+
for (const part of header.split(',')) {
|
|
49
|
+
const enc = part.split(';')[0]?.trim().toLowerCase()
|
|
50
|
+
if (enc) out.add(enc)
|
|
51
|
+
}
|
|
52
|
+
return out
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* if the request maps to a file with a content-encoded sibling
|
|
57
|
+
* (foo.js → foo.js.br) AND the sibling is at least as new as the source,
|
|
58
|
+
* return { encoding, ext } so the caller can serve the sibling directly
|
|
59
|
+
* with the right Content-Encoding header.
|
|
60
|
+
*
|
|
61
|
+
* stale siblings (mtime older than source) are deliberately skipped —
|
|
62
|
+
* a build that produced new source without regenerating .br is buggy,
|
|
63
|
+
* but we should never serve stale bytes as if they were fresh.
|
|
64
|
+
*/
|
|
65
|
+
function pickFreshSibling(
|
|
66
|
+
sourceFsPath: string,
|
|
67
|
+
acceptEncoding: string | undefined
|
|
68
|
+
): { encoding: string; ext: string } | undefined {
|
|
69
|
+
const accepted = parseAcceptEncoding(acceptEncoding)
|
|
70
|
+
if (accepted.size === 0) return undefined
|
|
71
|
+
|
|
72
|
+
const sourceStat = tryStat(sourceFsPath)
|
|
73
|
+
if (!sourceStat || !sourceStat.isFile()) return undefined
|
|
74
|
+
|
|
75
|
+
// only serve precompressed for compressible types — same gate hono uses
|
|
76
|
+
const mime = getMimeType(sourceFsPath)
|
|
77
|
+
if (mime && !compressibleMimeRe.test(mime)) return undefined
|
|
78
|
+
|
|
79
|
+
for (const candidate of PRECOMPRESSED_ENCODINGS) {
|
|
80
|
+
if (!accepted.has(candidate.encoding)) continue
|
|
81
|
+
const siblingStat = tryStat(sourceFsPath + candidate.ext)
|
|
82
|
+
if (!siblingStat || !siblingStat.isFile()) continue
|
|
83
|
+
// mtime guard: sibling must not predate the source. equal mtime is fine
|
|
84
|
+
// (some build systems set both to the same timestamp).
|
|
85
|
+
if (siblingStat.mtimeMs >= sourceStat.mtimeMs) {
|
|
86
|
+
return candidate
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return undefined
|
|
90
|
+
}
|
|
10
91
|
|
|
11
92
|
export type CompiledCacheRules = { re: RegExp; values: string[] }
|
|
12
93
|
|
|
@@ -61,14 +142,44 @@ export async function serveStaticAssets({
|
|
|
61
142
|
// path.join normalizes "./" away, so pre-compute the normalized prefix
|
|
62
143
|
const rootPrefix = `${outDir}/client/`
|
|
63
144
|
|
|
145
|
+
// pick a precompressed sibling if one is fresh + accepted. resolved
|
|
146
|
+
// relative to `root` so it matches what serveStatic will stat.
|
|
147
|
+
const reqPath = context.req.path.replace(/^\/+/, '')
|
|
148
|
+
const sibling = pickFreshSibling(
|
|
149
|
+
join(root, reqPath),
|
|
150
|
+
context.req.header('Accept-Encoding')
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
// when serving a sibling, the original mime is needed so onFound can
|
|
154
|
+
// restore Content-Type after serveStatic's mime guess for ".br"/".gz".
|
|
155
|
+
const originalMime = sibling ? getMimeType(reqPath) : undefined
|
|
156
|
+
|
|
64
157
|
const response = await serveStatic({
|
|
65
158
|
root,
|
|
159
|
+
// when a fresh sibling exists, pin the served file to <path>.<ext>.
|
|
160
|
+
// hono will stat that path and stream it as-is; we set the right
|
|
161
|
+
// Content-Encoding + Content-Type in onFound.
|
|
162
|
+
path: sibling ? reqPath + sibling.ext : undefined,
|
|
66
163
|
onFound: (fsPath, c) => {
|
|
164
|
+
// strip any encoding suffix so cache-rule patterns fire on the
|
|
165
|
+
// original logical asset, not the .br/.gz/.zst variant
|
|
166
|
+
const originalFsPath = fsPath.replace(precompressedSuffixRe, '')
|
|
167
|
+
|
|
168
|
+
if (sibling) {
|
|
169
|
+
c.header('Content-Encoding', sibling.encoding)
|
|
170
|
+
c.header('Vary', 'Accept-Encoding', { append: true })
|
|
171
|
+
// serveStatic set Content-Type from the .br extension (octet-stream
|
|
172
|
+
// for unknown). restore the original mime.
|
|
173
|
+
if (originalMime) {
|
|
174
|
+
c.header('Content-Type', originalMime)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
67
178
|
// onFound receives the joined fs path (e.g. "dist/client/foo.js")
|
|
68
179
|
// strip root prefix to get the URL-relative path for glob matching
|
|
69
|
-
const path =
|
|
70
|
-
?
|
|
71
|
-
:
|
|
180
|
+
const path = originalFsPath.startsWith(rootPrefix)
|
|
181
|
+
? originalFsPath.slice(rootPrefix.length)
|
|
182
|
+
: originalFsPath
|
|
72
183
|
|
|
73
184
|
// single regex test for all custom rules
|
|
74
185
|
if (cacheRules) {
|
|
@@ -84,7 +195,7 @@ export async function serveStaticAssets({
|
|
|
84
195
|
}
|
|
85
196
|
}
|
|
86
197
|
|
|
87
|
-
if (assetsPathRe.test(
|
|
198
|
+
if (assetsPathRe.test(originalFsPath) || hashedAssetRe.test(originalFsPath)) {
|
|
88
199
|
c.header('Cache-Control', 'public, immutable, max-age=31536000')
|
|
89
200
|
} else {
|
|
90
201
|
c.header('Cache-Control', 'public, max-age=0, must-revalidate')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serveStaticAssets.d.ts","sourceRoot":"","sources":["../../src/exports/serveStaticAssets.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"serveStaticAssets.d.ts","sourceRoot":"","sources":["../../src/exports/serveStaticAssets.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AA0FnC,MAAM,MAAM,kBAAkB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAA;AAEjE;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACnC,kBAAkB,CAyBpB;AAED,wBAAsB,iBAAiB,CAAC,EACtC,OAAO,EACP,IAAI,EACJ,MAAe,EACf,UAAU,GACX,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,kBAAkB,CAAA;CAChC,iCA4EA"}
|