UNPKG

ethercalc

Version:

Multi-User Spreadsheet Server — TypeScript rewrite (Cloudflare fullstack)

92 lines (77 loc) 2.88 kB
/** * In-process `MigrationTarget`. Recorded writes are available on the * public readonly maps so tests can assert the exact migration product * without any wrangler or network plumbing. * * Key layout mirrors AGENTS.md §10.2 + `@ethercalc/shared/storage-keys`: * doStorage[<room>][<STORAGE_KEYS.snapshot | logKey | auditKey | chatKey | ecellKey>] * d1Rooms[<room>] → { updated_at } * kvRoomsExists[<room>] → "1" * so an assertion can just compare the populated maps against a fixture. */ import { STORAGE_KEYS, logKey, auditKey, chatKey, ecellKey, } from '@ethercalc/shared/storage-keys'; import type { MigrationTarget } from '../apply.ts'; export interface InMemoryRoomIndexRow { updatedAt: number; } export interface InMemoryTargetOptions { /** Clock for `meta:updated_at` writes. Defaults to `Date.now`. */ now?: () => number; } /** * Implementation of {@link MigrationTarget} that stashes everything in * regular JS maps. All methods return resolved Promises so they compose * with the async applyRoomStream() pipeline. */ export class InMemoryTarget implements MigrationTarget { /** `doStorage.get(room)` → (storageKey → value). */ public readonly doStorage: Map<string, Map<string, string>> = new Map(); /** D1 `rooms` mirror. */ public readonly d1Rooms: Map<string, InMemoryRoomIndexRow> = new Map(); /** KV `rooms:exists:<room>` → "1". */ public readonly kvRoomsExists: Map<string, string> = new Map(); private readonly now: () => number; constructor(opts: InMemoryTargetOptions = {}) { this.now = opts.now ?? Date.now; } putSnapshot(room: string, snapshot: string): Promise<void> { this.bucket(room).set(STORAGE_KEYS.snapshot, snapshot); this.bucket(room).set(STORAGE_KEYS.metaUpdatedAt, String(this.now())); return Promise.resolve(); } putLog(room: string, seq: number, cmd: string): Promise<void> { this.bucket(room).set(logKey(seq), cmd); return Promise.resolve(); } putAudit(room: string, seq: number, cmd: string): Promise<void> { this.bucket(room).set(auditKey(seq), cmd); return Promise.resolve(); } putChat(room: string, seq: number, msg: string): Promise<void> { this.bucket(room).set(chatKey(seq), msg); return Promise.resolve(); } putEcell(room: string, user: string, cell: string): Promise<void> { this.bucket(room).set(ecellKey(user), cell); return Promise.resolve(); } setRoomIndex(room: string, updatedAt: number): Promise<void> { this.d1Rooms.set(room, { updatedAt }); this.kvRoomsExists.set(room, '1'); return Promise.resolve(); } private bucket(room: string): Map<string, string> { let b = this.doStorage.get(room); if (b === undefined) { b = new Map(); this.doStorage.set(room, b); } return b; } }