| 1 | export type InteractionDraftStore<Value> = { |
| 2 | read(key: string): Value | undefined; |
| 3 | write(key: string, value: Value): void; |
| 4 | delete(key: string): void; |
| 5 | releaseScope(scope: string): void; |
| 6 | size(): number; |
| 7 | }; |
| 8 | |
| 9 | /** Request-owned, bounded volatile state. Entries survive a component remount, |
| 10 | * but cannot become an unbounded process-global cache. */ |
| 11 | export function createInteractionDraftStore<Value>(limit = 128): InteractionDraftStore<Value> { |
| 12 | const entries = new Map<string, { scope: string; value: Value }>(); |
| 13 | const touch = (key: string, entry: { scope: string; value: Value }) => { |
| 14 | entries.delete(key); |
| 15 | entries.set(key, entry); |
| 16 | }; |
| 17 | return { |
| 18 | read(key) { |
| 19 | const entry = entries.get(key); |
| 20 | if (!entry) return undefined; |
| 21 | touch(key, entry); |
| 22 | return entry.value; |
| 23 | }, |
| 24 | write(key, value) { |
| 25 | const split = key.indexOf("\u0000"); |
| 26 | touch(key, { scope: split < 0 ? key : key.slice(0, split), value }); |
| 27 | while (entries.size > limit) entries.delete(entries.keys().next().value!); |
| 28 | }, |
| 29 | delete(key) { entries.delete(key); }, |
| 30 | releaseScope(scope) { |
| 31 | for (const [key, entry] of entries) if (entry.scope === scope) entries.delete(key); |
| 32 | }, |
| 33 | size: () => entries.size, |
| 34 | }; |
| 35 | } |
| 36 |