syncorejs 0.2.3 → 0.2.4
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/_vendor/core/cli.d.mts.map +1 -1
- package/dist/_vendor/core/cli.mjs +272 -7
- package/dist/_vendor/core/cli.mjs.map +1 -1
- package/dist/_vendor/core/index.d.mts +3 -3
- package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
- package/dist/_vendor/core/runtime/devtools.mjs +131 -0
- package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
- package/dist/_vendor/core/runtime/functions.d.mts +3 -3
- package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs +1 -1
- package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs +1 -1
- package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/shared.mjs +5 -1
- package/dist/_vendor/core/runtime/internal/engines/shared.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs +99 -13
- package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs +38 -4
- package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs.map +1 -1
- package/dist/_vendor/core/runtime/runtime.d.mts +65 -8
- package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
- package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
- package/dist/_vendor/core/transport.d.mts.map +1 -1
- package/dist/_vendor/core/transport.mjs +30 -5
- package/dist/_vendor/core/transport.mjs.map +1 -1
- package/dist/_vendor/devtools-protocol/index.d.ts +75 -1
- package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
- package/dist/_vendor/devtools-protocol/index.js.map +1 -1
- package/dist/_vendor/next/index.js +9 -1
- package/dist/_vendor/next/index.js.map +1 -1
- package/dist/_vendor/platform-expo/index.d.ts +1 -1
- package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
- package/dist/_vendor/platform-expo/index.js +6 -1
- package/dist/_vendor/platform-expo/index.js.map +1 -1
- package/dist/_vendor/platform-node/index.d.mts +2 -1
- package/dist/_vendor/platform-node/index.d.mts.map +1 -1
- package/dist/_vendor/platform-node/index.mjs +27 -2
- package/dist/_vendor/platform-node/index.mjs.map +1 -1
- package/dist/_vendor/platform-node/ipc-react.mjs +4 -0
- package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
- package/dist/_vendor/platform-web/external-change.d.ts +2 -2
- package/dist/_vendor/platform-web/external-change.js +2 -2
- package/dist/_vendor/platform-web/external-change.js.map +1 -1
- package/dist/_vendor/platform-web/index.d.ts +13 -10
- package/dist/_vendor/platform-web/index.d.ts.map +1 -1
- package/dist/_vendor/platform-web/index.js +66 -10
- package/dist/_vendor/platform-web/index.js.map +1 -1
- package/dist/_vendor/platform-web/indexeddb.d.ts +3 -3
- package/dist/_vendor/platform-web/indexeddb.js +3 -3
- package/dist/_vendor/platform-web/indexeddb.js.map +1 -1
- package/dist/_vendor/platform-web/opfs.d.ts +3 -1
- package/dist/_vendor/platform-web/opfs.d.ts.map +1 -1
- package/dist/_vendor/platform-web/opfs.js +29 -3
- package/dist/_vendor/platform-web/opfs.js.map +1 -1
- package/dist/_vendor/platform-web/persistence.d.ts +31 -1
- package/dist/_vendor/platform-web/persistence.d.ts.map +1 -1
- package/dist/_vendor/platform-web/persistence.js.map +1 -1
- package/dist/_vendor/platform-web/react.d.ts.map +1 -1
- package/dist/_vendor/platform-web/react.js +9 -1
- package/dist/_vendor/platform-web/react.js.map +1 -1
- package/dist/_vendor/react/index.d.ts +6 -5
- package/dist/_vendor/react/index.d.ts.map +1 -1
- package/dist/_vendor/react/index.js +6 -5
- package/dist/_vendor/react/index.js.map +1 -1
- package/dist/_vendor/svelte/index.d.ts +8 -6
- package/dist/_vendor/svelte/index.d.ts.map +1 -1
- package/dist/_vendor/svelte/index.js +7 -5
- package/dist/_vendor/svelte/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.mts","names":[],"sources":["../src/cli.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"cli.d.mts","names":[],"sources":["../src/cli.ts"],"mappings":"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"}
|
|
@@ -4,7 +4,8 @@ import { SyncoreRuntime } from "./runtime/runtime.mjs";
|
|
|
4
4
|
import { createDevtoolsCommandHandler, createDevtoolsSubscriptionHost } from "./runtime/devtools.mjs";
|
|
5
5
|
import { src_exports } from "./index.mjs";
|
|
6
6
|
import { generateDevtoolsToken, isAllowedDashboardOrigin, isAuthorizedDashboardRequest, sanitizeDevtoolsToken } from "./devtools-auth.mjs";
|
|
7
|
-
import { appendFile, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
7
|
+
import { appendFile, mkdir, open, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
8
9
|
import { createServer } from "node:http";
|
|
9
10
|
import { connect } from "node:net";
|
|
10
11
|
import path from "node:path";
|
|
@@ -36,6 +37,9 @@ const VALID_SYNCORE_TEMPLATES = [
|
|
|
36
37
|
let pendingDevBootstrap;
|
|
37
38
|
let devBootstrapInFlight = false;
|
|
38
39
|
const PROJECT_TARGET_RUNTIME_ID = "syncore-project-target";
|
|
40
|
+
const STORAGE_ACCESS_TICKET_TTL_MS = 300 * 1e3;
|
|
41
|
+
const STORAGE_ACCESS_CHUNK_BYTES = 1024 * 1024;
|
|
42
|
+
const STORAGE_ACCESS_MAX_PREVIEW_BYTES = 8e4;
|
|
39
43
|
program.name("syncorejs").description("Syncore local-first toolkit CLI").version("0.1.0");
|
|
40
44
|
program.command("init").description("Scaffold Syncore in the current directory").option("--template <template>", `Template to scaffold (${VALID_SYNCORE_TEMPLATES.join(", ")}, or auto)`, "auto").option("--force", "Overwrite Syncore-managed files when they already exist").action(async (options) => {
|
|
41
45
|
const cwd = process.cwd();
|
|
@@ -1161,6 +1165,19 @@ var HubFileStorageAdapter = class {
|
|
|
1161
1165
|
return null;
|
|
1162
1166
|
}
|
|
1163
1167
|
}
|
|
1168
|
+
async readRange(id, offset, length) {
|
|
1169
|
+
let handle;
|
|
1170
|
+
try {
|
|
1171
|
+
handle = await open(this.filePath(id), "r");
|
|
1172
|
+
const buffer = Buffer.alloc(Math.max(length, 0));
|
|
1173
|
+
const result = await handle.read(buffer, 0, buffer.byteLength, Math.max(offset, 0));
|
|
1174
|
+
return buffer.subarray(0, result.bytesRead);
|
|
1175
|
+
} catch {
|
|
1176
|
+
return null;
|
|
1177
|
+
} finally {
|
|
1178
|
+
await handle?.close();
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1164
1181
|
async delete(id) {
|
|
1165
1182
|
await rm(this.filePath(id), { force: true });
|
|
1166
1183
|
}
|
|
@@ -1252,6 +1269,11 @@ async function createProjectTargetBackend(cwd, externalChangeSignal) {
|
|
|
1252
1269
|
driver,
|
|
1253
1270
|
storage: new HubFileStorageAdapter(storageDirectory),
|
|
1254
1271
|
platform: "project",
|
|
1272
|
+
runtimeCapabilities: { storage: {
|
|
1273
|
+
available: true,
|
|
1274
|
+
protocol: "file",
|
|
1275
|
+
supportsRange: true
|
|
1276
|
+
} },
|
|
1255
1277
|
...externalChangeSignal ? { externalChangeSignal } : {}
|
|
1256
1278
|
});
|
|
1257
1279
|
await runtime.start();
|
|
@@ -1310,6 +1332,13 @@ function createProjectDevtoolsCapabilities() {
|
|
|
1310
1332
|
mutate: true,
|
|
1311
1333
|
importExport: true
|
|
1312
1334
|
},
|
|
1335
|
+
storage: {
|
|
1336
|
+
browse: true,
|
|
1337
|
+
download: true,
|
|
1338
|
+
readRange: true,
|
|
1339
|
+
delete: true,
|
|
1340
|
+
maxPreviewBytes: 8e4
|
|
1341
|
+
},
|
|
1313
1342
|
scheduler: {
|
|
1314
1343
|
read: true,
|
|
1315
1344
|
edit: true
|
|
@@ -1497,14 +1526,15 @@ async function startDevHub(options) {
|
|
|
1497
1526
|
return await readDevtoolsSessionState(options.cwd) ?? sessionState;
|
|
1498
1527
|
}
|
|
1499
1528
|
await writeDevtoolsSessionState(options.cwd, sessionState);
|
|
1500
|
-
const httpServer = createServer((
|
|
1501
|
-
response.
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
}));
|
|
1529
|
+
const httpServer = createServer((request, response) => {
|
|
1530
|
+
handleStorageAccessHttpRequest(request, response).catch((error) => {
|
|
1531
|
+
if (!response.headersSent) writeJsonResponse(response, 500, { error: formatError(error) });
|
|
1532
|
+
else response.destroy(error instanceof Error ? error : void 0);
|
|
1533
|
+
});
|
|
1506
1534
|
});
|
|
1507
1535
|
const websocketServer = new WebSocketServer({ server: httpServer });
|
|
1536
|
+
const storageAccessTickets = /* @__PURE__ */ new Map();
|
|
1537
|
+
const pendingHubCommands = /* @__PURE__ */ new Map();
|
|
1508
1538
|
const runtimeSockets = /* @__PURE__ */ new Map();
|
|
1509
1539
|
const runtimeHellos = /* @__PURE__ */ new Map();
|
|
1510
1540
|
const runtimeEvents = /* @__PURE__ */ new Map();
|
|
@@ -1571,6 +1601,135 @@ async function startDevHub(options) {
|
|
|
1571
1601
|
event
|
|
1572
1602
|
})}\n`);
|
|
1573
1603
|
};
|
|
1604
|
+
const requestRuntimeCommand = async (targetRuntimeId, payload, timeoutMs = 3e4) => {
|
|
1605
|
+
if (targetRuntimeId === PROJECT_TARGET_RUNTIME_ID && projectTargetBackend) return projectTargetBackend.handleCommand(payload);
|
|
1606
|
+
const target = runtimeSockets.get(targetRuntimeId);
|
|
1607
|
+
if (!target || target.readyState !== WebSocket.OPEN) throw new Error(`Runtime ${targetRuntimeId} is not connected.`);
|
|
1608
|
+
const commandId = `hub:${randomUUID()}`;
|
|
1609
|
+
return await new Promise((resolve, reject) => {
|
|
1610
|
+
const timeout = setTimeout(() => {
|
|
1611
|
+
pendingHubCommands.delete(commandId);
|
|
1612
|
+
reject(/* @__PURE__ */ new Error(`Runtime command ${payload.kind} timed out.`));
|
|
1613
|
+
}, timeoutMs);
|
|
1614
|
+
pendingHubCommands.set(commandId, {
|
|
1615
|
+
resolve,
|
|
1616
|
+
reject,
|
|
1617
|
+
timeout
|
|
1618
|
+
});
|
|
1619
|
+
target.send(JSON.stringify({
|
|
1620
|
+
type: "command",
|
|
1621
|
+
commandId,
|
|
1622
|
+
targetRuntimeId,
|
|
1623
|
+
payload
|
|
1624
|
+
}));
|
|
1625
|
+
});
|
|
1626
|
+
};
|
|
1627
|
+
const createStorageAccessTicket = async (targetRuntimeId, id, purpose) => {
|
|
1628
|
+
const metadata = await requestRuntimeCommand(targetRuntimeId, {
|
|
1629
|
+
kind: "storage.readRange",
|
|
1630
|
+
id,
|
|
1631
|
+
offset: 0,
|
|
1632
|
+
length: 0
|
|
1633
|
+
});
|
|
1634
|
+
if (metadata.kind !== "storage.readRange.result") return {
|
|
1635
|
+
kind: "storage.access.create.result",
|
|
1636
|
+
error: "Runtime returned an unexpected storage access response."
|
|
1637
|
+
};
|
|
1638
|
+
if (metadata.error || !metadata.entry) return {
|
|
1639
|
+
kind: "storage.access.create.result",
|
|
1640
|
+
error: metadata.error ?? "Storage object could not be accessed."
|
|
1641
|
+
};
|
|
1642
|
+
const ticket = randomUUID();
|
|
1643
|
+
const expiresAt = Date.now() + STORAGE_ACCESS_TICKET_TTL_MS;
|
|
1644
|
+
storageAccessTickets.set(ticket, {
|
|
1645
|
+
id: ticket,
|
|
1646
|
+
runtimeId: targetRuntimeId,
|
|
1647
|
+
storageId: id,
|
|
1648
|
+
purpose,
|
|
1649
|
+
entry: metadata.entry,
|
|
1650
|
+
supportsRange: metadata.supportsRange,
|
|
1651
|
+
expiresAt
|
|
1652
|
+
});
|
|
1653
|
+
cleanupExpiredStorageTickets();
|
|
1654
|
+
return {
|
|
1655
|
+
kind: "storage.access.create.result",
|
|
1656
|
+
entry: metadata.entry,
|
|
1657
|
+
url: `http://127.0.0.1:${devtoolsPort}/storage/access/${ticket}`,
|
|
1658
|
+
expiresAt,
|
|
1659
|
+
supportsRange: metadata.supportsRange,
|
|
1660
|
+
maxPreviewBytes: STORAGE_ACCESS_MAX_PREVIEW_BYTES
|
|
1661
|
+
};
|
|
1662
|
+
};
|
|
1663
|
+
const cleanupExpiredStorageTickets = () => {
|
|
1664
|
+
const now = Date.now();
|
|
1665
|
+
for (const [ticket, access] of storageAccessTickets) if (access.expiresAt <= now) storageAccessTickets.delete(ticket);
|
|
1666
|
+
};
|
|
1667
|
+
const handleStorageAccessHttpRequest = async (request, response) => {
|
|
1668
|
+
setStorageCorsHeaders(response);
|
|
1669
|
+
if (request.method === "OPTIONS") {
|
|
1670
|
+
response.writeHead(204);
|
|
1671
|
+
response.end();
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1674
|
+
const requestUrl = new URL(request.url ?? "/", `http://127.0.0.1:${devtoolsPort}`);
|
|
1675
|
+
const match = /^\/storage\/access\/([^/]+)$/.exec(requestUrl.pathname);
|
|
1676
|
+
if (!match) {
|
|
1677
|
+
writeJsonResponse(response, 200, {
|
|
1678
|
+
ok: true,
|
|
1679
|
+
wsPort: devtoolsPort
|
|
1680
|
+
});
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
1684
|
+
writeTextResponse(response, 405, "Method not allowed.");
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1687
|
+
cleanupExpiredStorageTickets();
|
|
1688
|
+
const ticket = storageAccessTickets.get(match[1]);
|
|
1689
|
+
if (!ticket || ticket.expiresAt <= Date.now()) {
|
|
1690
|
+
writeTextResponse(response, 401, "Storage access ticket is invalid or expired.");
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
const range = parseStorageRangeHeader(request.headers.range, ticket.entry.size);
|
|
1694
|
+
if ("error" in range) {
|
|
1695
|
+
writeTextResponse(response, range.status, range.error);
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1698
|
+
const start = range.start;
|
|
1699
|
+
const end = range.end;
|
|
1700
|
+
const byteLength = end >= start ? end - start + 1 : 0;
|
|
1701
|
+
if (!ticket.supportsRange && ticket.entry.size > STORAGE_ACCESS_CHUNK_BYTES) {
|
|
1702
|
+
writeTextResponse(response, 409, "This storage backend does not support streaming large files.");
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
writeStorageAccessHeaders(response, ticket, {
|
|
1706
|
+
status: range.partial ? 206 : 200,
|
|
1707
|
+
start,
|
|
1708
|
+
end,
|
|
1709
|
+
byteLength
|
|
1710
|
+
});
|
|
1711
|
+
if (request.method === "HEAD") {
|
|
1712
|
+
response.end();
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
let offset = start;
|
|
1716
|
+
while (offset <= end) {
|
|
1717
|
+
const chunkLength = Math.min(STORAGE_ACCESS_CHUNK_BYTES, end - offset + 1);
|
|
1718
|
+
const chunk = await requestRuntimeCommand(ticket.runtimeId, {
|
|
1719
|
+
kind: "storage.readRange",
|
|
1720
|
+
id: ticket.storageId,
|
|
1721
|
+
offset,
|
|
1722
|
+
length: chunkLength
|
|
1723
|
+
}, 6e4);
|
|
1724
|
+
if (chunk.kind !== "storage.readRange.result") throw new Error("Runtime returned an unexpected storage chunk response.");
|
|
1725
|
+
if (chunk.error || !chunk.base64) throw new Error(chunk.error ?? "Storage chunk could not be read.");
|
|
1726
|
+
const bytes = Buffer.from(chunk.base64, "base64");
|
|
1727
|
+
if (bytes.byteLength === 0) break;
|
|
1728
|
+
await writeResponseChunk(response, bytes);
|
|
1729
|
+
offset += bytes.byteLength;
|
|
1730
|
+
}
|
|
1731
|
+
response.end();
|
|
1732
|
+
};
|
|
1574
1733
|
websocketServer.on("connection", (socket, request) => {
|
|
1575
1734
|
const isBrowserDashboardClient = isAllowedDashboardOrigin(request.headers.origin, dashboardPort);
|
|
1576
1735
|
const isAuthorizedDashboardClient = !isBrowserDashboardClient || isAuthorizedDashboardRequest({
|
|
@@ -1616,6 +1775,29 @@ async function startDevHub(options) {
|
|
|
1616
1775
|
if (!isAuthorizedDashboardClient) return;
|
|
1617
1776
|
const targetRuntimeId = message.targetRuntimeId;
|
|
1618
1777
|
if (!targetRuntimeId) return;
|
|
1778
|
+
if (message.payload.kind === "storage.access.create") {
|
|
1779
|
+
createStorageAccessTicket(targetRuntimeId, message.payload.id, message.payload.purpose).then((payload) => {
|
|
1780
|
+
if (socket.readyState !== WebSocket.OPEN) return;
|
|
1781
|
+
socket.send(JSON.stringify({
|
|
1782
|
+
type: "command.result",
|
|
1783
|
+
commandId: message.commandId,
|
|
1784
|
+
runtimeId: targetRuntimeId,
|
|
1785
|
+
payload
|
|
1786
|
+
}));
|
|
1787
|
+
}).catch((error) => {
|
|
1788
|
+
if (socket.readyState !== WebSocket.OPEN) return;
|
|
1789
|
+
socket.send(JSON.stringify({
|
|
1790
|
+
type: "command.result",
|
|
1791
|
+
commandId: message.commandId,
|
|
1792
|
+
runtimeId: targetRuntimeId,
|
|
1793
|
+
payload: {
|
|
1794
|
+
kind: "storage.access.create.result",
|
|
1795
|
+
error: formatError(error)
|
|
1796
|
+
}
|
|
1797
|
+
}));
|
|
1798
|
+
});
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1619
1801
|
if (targetRuntimeId === PROJECT_TARGET_RUNTIME_ID && projectTargetBackend) {
|
|
1620
1802
|
(async () => {
|
|
1621
1803
|
const payload = await projectTargetBackend.handleCommand(message.payload);
|
|
@@ -1719,6 +1901,15 @@ async function startDevHub(options) {
|
|
|
1719
1901
|
appendHubLog(message.event);
|
|
1720
1902
|
} else if (message.type === "event") appendHubLog(message.event);
|
|
1721
1903
|
if (message.type === "command.result" || message.type === "subscription.data" || message.type === "subscription.error") {
|
|
1904
|
+
if (message.type === "command.result") {
|
|
1905
|
+
const pending = pendingHubCommands.get(message.commandId);
|
|
1906
|
+
if (pending) {
|
|
1907
|
+
clearTimeout(pending.timeout);
|
|
1908
|
+
pendingHubCommands.delete(message.commandId);
|
|
1909
|
+
pending.resolve(message.payload);
|
|
1910
|
+
return;
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1722
1913
|
for (const client of dashboardSockets) if (client.readyState === WebSocket.OPEN) client.send(encoded);
|
|
1723
1914
|
return;
|
|
1724
1915
|
}
|
|
@@ -1808,6 +1999,80 @@ async function startDevHub(options) {
|
|
|
1808
1999
|
process.on("SIGTERM", close);
|
|
1809
2000
|
return sessionState;
|
|
1810
2001
|
}
|
|
2002
|
+
function setStorageCorsHeaders(response) {
|
|
2003
|
+
response.setHeader("Access-Control-Allow-Origin", "*");
|
|
2004
|
+
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
|
|
2005
|
+
response.setHeader("Access-Control-Allow-Headers", "Range");
|
|
2006
|
+
response.setHeader("Access-Control-Expose-Headers", "Accept-Ranges, Content-Disposition, Content-Length, Content-Range, Content-Type");
|
|
2007
|
+
}
|
|
2008
|
+
function writeJsonResponse(response, status, payload) {
|
|
2009
|
+
response.writeHead(status, { "content-type": "application/json" });
|
|
2010
|
+
response.end(JSON.stringify(payload));
|
|
2011
|
+
}
|
|
2012
|
+
function writeTextResponse(response, status, message) {
|
|
2013
|
+
response.writeHead(status, { "content-type": "text/plain; charset=utf-8" });
|
|
2014
|
+
response.end(message);
|
|
2015
|
+
}
|
|
2016
|
+
function parseStorageRangeHeader(header, size) {
|
|
2017
|
+
if (size === 0) {
|
|
2018
|
+
if (!header) return {
|
|
2019
|
+
start: 0,
|
|
2020
|
+
end: -1,
|
|
2021
|
+
partial: false
|
|
2022
|
+
};
|
|
2023
|
+
return {
|
|
2024
|
+
error: "Requested range is not satisfiable.",
|
|
2025
|
+
status: 416
|
|
2026
|
+
};
|
|
2027
|
+
}
|
|
2028
|
+
if (!header) return {
|
|
2029
|
+
start: 0,
|
|
2030
|
+
end: size - 1,
|
|
2031
|
+
partial: false
|
|
2032
|
+
};
|
|
2033
|
+
if (header.includes(",")) return {
|
|
2034
|
+
error: "Multi-range requests are not supported.",
|
|
2035
|
+
status: 416
|
|
2036
|
+
};
|
|
2037
|
+
const match = /^bytes=(\d+)-(\d*)$/.exec(header);
|
|
2038
|
+
if (!match) return {
|
|
2039
|
+
error: "Only simple byte ranges are supported.",
|
|
2040
|
+
status: 416
|
|
2041
|
+
};
|
|
2042
|
+
const start = Number(match[1]);
|
|
2043
|
+
const end = match[2] ? Number(match[2]) : size - 1;
|
|
2044
|
+
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start || start >= size) return {
|
|
2045
|
+
error: "Requested range is not satisfiable.",
|
|
2046
|
+
status: 416
|
|
2047
|
+
};
|
|
2048
|
+
return {
|
|
2049
|
+
start,
|
|
2050
|
+
end: Math.min(end, size - 1),
|
|
2051
|
+
partial: true
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
function writeStorageAccessHeaders(response, ticket, range) {
|
|
2055
|
+
const headers = {
|
|
2056
|
+
"content-type": ticket.entry.contentType ?? "application/octet-stream",
|
|
2057
|
+
"content-length": range.byteLength,
|
|
2058
|
+
"accept-ranges": ticket.supportsRange ? "bytes" : "none",
|
|
2059
|
+
"content-disposition": renderStorageContentDisposition(ticket)
|
|
2060
|
+
};
|
|
2061
|
+
if (range.status === 206) headers["content-range"] = `bytes ${range.start}-${range.end}/${ticket.entry.size}`;
|
|
2062
|
+
response.writeHead(range.status, headers);
|
|
2063
|
+
}
|
|
2064
|
+
function renderStorageContentDisposition(ticket) {
|
|
2065
|
+
return `${ticket.purpose === "download" ? "attachment" : "inline"}; filename="${sanitizeHeaderFilename(ticket.entry.fileName ?? ticket.entry.id)}"; filename*=UTF-8''${encodeURIComponent(ticket.entry.fileName ?? `${ticket.entry.id}.bin`)}`;
|
|
2066
|
+
}
|
|
2067
|
+
function sanitizeHeaderFilename(value) {
|
|
2068
|
+
return value.replaceAll(/["\\\r\n]/g, "_");
|
|
2069
|
+
}
|
|
2070
|
+
async function writeResponseChunk(response, chunk) {
|
|
2071
|
+
if (response.write(chunk)) return;
|
|
2072
|
+
await new Promise((resolve) => {
|
|
2073
|
+
response.once("drain", resolve);
|
|
2074
|
+
});
|
|
2075
|
+
}
|
|
1811
2076
|
async function writeDevtoolsSessionState(cwd, state) {
|
|
1812
2077
|
const sessionPath = path.join(cwd, DEVTOOLS_SESSION_FILE);
|
|
1813
2078
|
await mkdir(path.dirname(sessionPath), { recursive: true });
|