@aletheia-ios/tools 0.2.1 → 0.2.2
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/cli.js +36 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1061,7 +1061,10 @@ const TRAVERSAL = /^(\.\.[/\\])+/;
|
|
|
1061
1061
|
/** How long after the last change to wait before rebuilding, so an editor's save burst is one build. */
|
|
1062
1062
|
const DEBOUNCE_MS = 300;
|
|
1063
1063
|
const OK = 200;
|
|
1064
|
+
const NOT_MODIFIED = 304;
|
|
1064
1065
|
const NOT_FOUND = 404;
|
|
1066
|
+
/** Base 16, so the validator stays short. */
|
|
1067
|
+
const HEX = 16;
|
|
1065
1068
|
/**
|
|
1066
1069
|
* Packs and indexes the whole repository, reporting failures instead of throwing.
|
|
1067
1070
|
*
|
|
@@ -1103,7 +1106,27 @@ async function resolveFile(dist, url) {
|
|
|
1103
1106
|
if (!(existsSync(file) && (await stat(file)).isFile())) return null;
|
|
1104
1107
|
return file;
|
|
1105
1108
|
}
|
|
1106
|
-
/**
|
|
1109
|
+
/**
|
|
1110
|
+
* A validator for the file as it is right now, from its size and modification time.
|
|
1111
|
+
*
|
|
1112
|
+
* Every rebuild rewrites the file, so the value changes and a conditional request gets the new
|
|
1113
|
+
* bytes. Between rebuilds it is stable, which is the only case that answers 304.
|
|
1114
|
+
*/
|
|
1115
|
+
function etagOf(size, mtimeMs) {
|
|
1116
|
+
return `"${size.toString(HEX)}-${Math.trunc(mtimeMs).toString(HEX)}"`;
|
|
1117
|
+
}
|
|
1118
|
+
/** Whether the request already holds this version. A client may offer several. */
|
|
1119
|
+
function offers(header, etag) {
|
|
1120
|
+
if (header === void 0) return false;
|
|
1121
|
+
return header.split(",").some((candidate) => candidate.trim() === etag);
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* A static server over `dist/`.
|
|
1125
|
+
*
|
|
1126
|
+
* Sends `cache-control: no-store` so nothing holds on to a dev build, alongside an `ETag` so a
|
|
1127
|
+
* client that asks can still be told nothing changed. The two are not in tension: `no-store`
|
|
1128
|
+
* governs whether a copy may be kept, the validator governs whether a fetch has to transfer.
|
|
1129
|
+
*/
|
|
1107
1130
|
function fileServer(dist) {
|
|
1108
1131
|
return createServer(async (request, response) => {
|
|
1109
1132
|
const file = await resolveFile(dist, request.url ?? "/");
|
|
@@ -1111,9 +1134,20 @@ function fileServer(dist) {
|
|
|
1111
1134
|
response.writeHead(NOT_FOUND).end();
|
|
1112
1135
|
return;
|
|
1113
1136
|
}
|
|
1137
|
+
const { size, mtimeMs } = await stat(file);
|
|
1138
|
+
const etag = etagOf(size, mtimeMs);
|
|
1139
|
+
if (offers(request.headers["if-none-match"], etag)) {
|
|
1140
|
+
response.writeHead(NOT_MODIFIED, {
|
|
1141
|
+
etag,
|
|
1142
|
+
"cache-control": "no-store"
|
|
1143
|
+
}).end();
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1114
1146
|
response.writeHead(OK, {
|
|
1115
1147
|
"content-type": TYPES[extname(file)] ?? "application/octet-stream",
|
|
1116
|
-
"cache-control": "no-store"
|
|
1148
|
+
"cache-control": "no-store",
|
|
1149
|
+
"content-length": size,
|
|
1150
|
+
etag
|
|
1117
1151
|
});
|
|
1118
1152
|
createReadStream(file).pipe(response);
|
|
1119
1153
|
});
|