# `memory.ts` _(no submodules)_ ## Purpose In-memory `UploadSessionStore` adapter. Tracks both in-flight `UploadSession` state (sessions Map) and completed `UploadRecord` metadata (records Map) so `GET /uploads/{uploadId}` works against either an in-progress chunked upload OR a completed single-shot upload uniformly. Used as the default store when `createFileUploadPlugin` is invoked without an explicit `sessionStore` option. ## Features - Idempotent `appendChunkIndex` via underlying `Set` — calling twice with the same index is a no-op. - `listExpired(now, ttlMs)` returns every session whose `createdAt + ttlMs <= now` so the plugin's cleanup loop has a single-call sweep API. - Strict `create` (rejects duplicates) and strict `update` (rejects missing) so logic bugs in the service layer surface immediately rather than silently writing the wrong record. - Records are stored separately from sessions so `complete` can swap session → record atomically without leaving an in-flight reference behind. ## Usage: Basic ```ts import { MemoryUploadSessionStore } from "@suaveplan/api-plugin-file-upload/session-store/memory"; const store = new MemoryUploadSessionStore(); await store.create(session); const fetched = await store.read(session.uploadId); ``` ## Usage: Advanced ```ts import { MemoryUploadSessionStore } from "@suaveplan/api-plugin-file-upload"; const store = new MemoryUploadSessionStore(); await store.create(session); await store.appendChunkIndex(session.uploadId, 0); await store.appendChunkIndex(session.uploadId, 0); // idempotent const expired = await store.listExpired(Date.now() + 60_000, 1000); for (const s of expired) await store.delete(s.uploadId); ``` ## API Reference - `class MemoryUploadSessionStore implements UploadSessionStore` — constructor takes no arguments. - `create(session)` / `read(id)` / `update(session)` / `delete(id)` — standard CRUD. - `appendChunkIndex(id, index): Promise` — returns the updated session. - `listExpired(now, ttlMs): Promise` — TTL sweep. - `readRecord(id)` / `saveRecord(record)` / `deleteRecord(id)` — record-side CRUD. ## Implementation Notes - The session and record maps are kept distinct because a single upload transitions session → record atomically; conflating them would force the caller to encode that transition with an enum field on a union type. - `appendChunkIndex` always returns a fresh `UploadSession` value (shallow-copied with the new set) so consumers comparing references can detect updates without `===` foot-guns. - `delete` is intentionally idempotent — abort routes call it without first checking existence.