SOURCE / PINNED RELEASE
Made of little things.
Body Journal
- Release
- e8049f8da4e2…
- Author-recorded commit
- 61c04bf0e8aa…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-6c4554844c7
Archive hash verified: 4369d778da046a8b…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
// One writer per mounted session. Edits during a write are coalesced into the next snapshot.
export class SessionStore<T> {
loading = true;
blocked = false;
clearing = false;
dirty = false;
message = 'Restoring session…';
private writing?: Promise<void>;
private stopped = false;
constructor(private io: {
read: () => Promise<string | null>; write: (raw: string) => Promise<void>; remove: () => Promise<void>;
parse: (raw: string) => T; snapshot: () => string; restore: (record: T) => void; reset: () => void; changed: () => void;
}) {}
async init(): Promise<void> {
this.loading = true; this.io.changed();
try {
const raw = await this.io.read();
if (this.stopped) return;
if (raw) this.io.restore(this.io.parse(raw));
this.blocked = false; this.dirty = false; this.message = raw ? 'Session restored · autosave on' : 'Autosave on';
} catch { this.blocked = true; this.message = 'Session could not be restored. Stored data was left untouched. Retry restoring, or save your current session to replace it.'; }
finally { this.loading = false; if (!this.stopped) this.io.changed(); }
}
change(): void {
if (this.loading || this.clearing || this.stopped) return;
this.dirty = true;
if (!this.blocked) void this.flush();
}
async flush(retry = false): Promise<void> {
if (this.loading || this.clearing || this.stopped) return;
if (retry) { this.blocked = false; this.dirty = true; }
if (this.blocked) return;
if (this.writing) return this.writing;
// The microtask lets all synchronous handlers finish before taking a snapshot.
this.writing = Promise.resolve().then(async () => {
while (this.dirty && !this.clearing && !this.stopped) {
this.dirty = false; this.message = 'Saving session…'; this.io.changed();
try { const raw = this.io.snapshot(); await this.io.write(raw); this.message = 'Session saved'; }
catch { this.dirty = true; this.blocked = true; this.message = 'Session not saved. Your work is still here. Open Session and retry saving.'; break; }
}
}).finally(() => {
this.writing = undefined;
if (!this.stopped) this.io.changed();
if (this.dirty && !this.blocked && !this.clearing && !this.stopped) void this.flush();
});
return this.writing;
}
async clear(): Promise<void> {
if (this.loading || this.clearing || this.stopped) return;
this.clearing = true; this.message = 'Clearing session…'; this.io.changed();
await this.writing;
try {
await this.io.remove();
this.io.reset(); this.dirty = false; this.blocked = false; this.message = 'Session cleared · autosave on';
} catch { this.blocked = true; this.message = 'Session could not be cleared. Your work is still here. Try again.'; }
finally { this.clearing = false; this.io.changed(); }
}
stop(): void { this.stopped = true; }
}
