mirror of
https://github.com/Nystik-gh/ignis.git
synced 2026-06-17 04:35:53 +00:00
keep small writes durable across page dismissal with fetch keepalive
This commit is contained in:
@@ -19,6 +19,18 @@ function vaultId() {
|
||||
return window.__currentVaultId || "";
|
||||
}
|
||||
|
||||
const KEEPALIVE_MAX_BYTES = 64 * 1024;
|
||||
|
||||
// keepalive lets a request finish after the page starts unloading.
|
||||
// Its body is capped at 64KB across a shared pool, so opt in only under that limit.
|
||||
function withinKeepaliveCap(body) {
|
||||
if (!body) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return new TextEncoder().encode(body).length <= KEEPALIVE_MAX_BYTES;
|
||||
}
|
||||
|
||||
async function request(method, endpoint, params = {}) {
|
||||
const url = new URL(API_BASE + endpoint, window.location.origin);
|
||||
|
||||
@@ -37,6 +49,11 @@ async function request(method, endpoint, params = {}) {
|
||||
options.body = JSON.stringify({ vault: vaultId(), ...params });
|
||||
}
|
||||
|
||||
// A write (POST/DELETE) opts into keepalive so a page dismissal does not drop it.
|
||||
if (method !== "GET" && withinKeepaliveCap(options.body)) {
|
||||
options.keepalive = true;
|
||||
}
|
||||
|
||||
const res = await fetch(url.toString(), options);
|
||||
if (!res.ok) {
|
||||
const err = await res
|
||||
|
||||
60
packages/shim/src/fs/transport.test.js
Normal file
60
packages/shim/src/fs/transport.test.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { transport } from "./transport.js";
|
||||
|
||||
let fetchMock;
|
||||
|
||||
beforeEach(() => {
|
||||
fetchMock = vi.fn(async () => ({
|
||||
ok: true,
|
||||
json: async () => ({}),
|
||||
text: async () => "",
|
||||
arrayBuffer: async () => new ArrayBuffer(0),
|
||||
}));
|
||||
globalThis.fetch = fetchMock;
|
||||
globalThis.window = {
|
||||
location: { origin: "http://localhost" },
|
||||
__currentVaultId: "v",
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
delete globalThis.fetch;
|
||||
delete globalThis.window;
|
||||
});
|
||||
|
||||
function lastInit() {
|
||||
return fetchMock.mock.calls.at(-1)[1];
|
||||
}
|
||||
|
||||
describe("transport keepalive gating", () => {
|
||||
it("sets keepalive on a small write", async () => {
|
||||
await transport.writeFile("a.md", "hello", "utf-8");
|
||||
|
||||
expect(lastInit().keepalive).toBe(true);
|
||||
});
|
||||
|
||||
it("omits keepalive when the body exceeds the 64KB cap", async () => {
|
||||
await transport.writeFile("a.md", "x".repeat(70 * 1024), "utf-8");
|
||||
|
||||
expect(lastInit().keepalive).toBeFalsy();
|
||||
});
|
||||
|
||||
it("counts base64 inflation against the cap for binary writes", async () => {
|
||||
// 60KB of bytes inflates to ~80KB of base64, over the cap.
|
||||
await transport.writeFile("a.bin", new Uint8Array(60 * 1024));
|
||||
|
||||
expect(lastInit().keepalive).toBeFalsy();
|
||||
});
|
||||
|
||||
it("sets keepalive on a bodyless delete", async () => {
|
||||
await transport.unlink("a.md");
|
||||
|
||||
expect(lastInit().keepalive).toBe(true);
|
||||
});
|
||||
|
||||
it("does not set keepalive on a read", async () => {
|
||||
await transport.readFile("a.md", "utf8");
|
||||
|
||||
expect(lastInit().keepalive).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user