components/media/media-autosave.ts
95 lines and 12 functions in DrDustinEdwards/capsomer, at commit d3966cc on 2026-10-24. This file on GitHub.
Functions
| Function | Lines | Complexity | Covered | CRAP | Mutation |
|---|---|---|---|---|---|
| createAutosave | 33 to 101 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| set | 39 to 42 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| stop | 43 to 46 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| run | 47 to 79 | 3 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| <anonymous:62> | 62 to 70 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| <anonymous:71> | 71 to 75 | 3 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| <anonymous:77> | 77 to 77 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| schedule | 81 to 86 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| <anonymous:85> | 85 to 85 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| flush | 87 to 94 | 4 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| retry | 95 to 97 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| state | 98 to 98 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
Imports
It imports no other module of this repo. Imported by components/media/media-inspector.ts, components/media/media.ts.
Source
// The media inspector's autosave.
// ---------------------------------------------------------------------------------------
// Autosave, pure. It waits until typing stops, saves, and says where it is. A change that
// arrives while a save runs is saved after it. Nothing here touches the fields, so what was
// typed is never lost: a failure only changes the state.
export type SaveState = "idle" | "dirty" | "saving" | "saved" | "failed";
export interface AutosaveOptions {
// Milliseconds of quiet before a save: 800.
delay?: number;
// Reads the fields and sends them. Called synchronously when a save starts, so what it reads is
// what the person had typed at that moment. Reject with an Error that says why.
save: () => Promise<void>;
onState?: (state: SaveState, error?: string) => void;
}
export interface Autosave {
// A change was made: save when typing stops.
schedule(): void;
// Save now, if there is anything unsaved (a field lost focus).
flush(): Promise<void>;
// Try again after a failure.
retry(): Promise<void>;
state(): SaveState;
// Forget the timer (the inspector moved to another file).
cancel(): void;
}
export const AUTOSAVE_MS = 800;
export function createAutosave(opts: AutosaveOptions): Autosave {
const delay = opts.delay ?? AUTOSAVE_MS;
let state: SaveState = "idle";
let timer: ReturnType<typeof setTimeout> | null = null;
let running: Promise<void> | null = null;
let again = false;
const set = (s: SaveState, error?: string) => {
state = s;
opts.onState?.(s, error);
};
const stop = () => {
if (timer !== null) clearTimeout(timer);
timer = null;
};
const run = (): Promise<void> => {
stop();
if (running) {
again = true;
return running;
}
set("saving");
let pending: Promise<void>;
try {
pending = opts.save();
} catch (err) {
pending = Promise.reject(err);
}
running = pending
.then(
() => {
running = null;
if (again) {
again = false;
return run();
}
set("saved");
return undefined;
},
(err: unknown) => {
running = null;
again = false;
set("failed", err instanceof Error && err.message ? err.message : "Nothing says why.");
},
)
.then(() => undefined);
return running;
};
return {
schedule() {
if (running) again = true;
else set("dirty");
stop();
timer = setTimeout(() => void run(), delay);
},
flush() {
if (running) {
again = again || state === "dirty";
return running;
}
if (state === "dirty") return run();
return Promise.resolve();
},
retry() {
return run();
},
state: () => state,
cancel: stop,
};
}