Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | 7x 7x 7x 7x 3x 3x 3x 5x 5x 3x 3x 2x 2x 3x 3x 3x 3x 3x 19x 19x 6x 6x 19x | /**
* 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 CLAUDE.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 applyRooms() 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;
}
}
|