web based infinite canvas
1import type { Timestamp } from "./repo";
2
3export type BoardStats = {
4 pageCount: number;
5 shapeCount: number;
6 bindingCount: number;
7 docSizeBytes: number;
8 lastUpdated: Timestamp;
9};
10
11export type SchemaInfo = { declaredVersion: number; installedVersion: number };
12
13export type MigrationInfo = { id: string; appliedAt: Timestamp };
14
15export type BoardInspectorData = {
16 stats: BoardStats;
17 schema: SchemaInfo;
18 migrations: MigrationInfo[];
19 pendingMigrations: string[];
20};
21
22/**
23 * Calculate board statistics from row counts and doc size.
24 */
25export const BoardStatsOps = {
26 create(
27 options: {
28 pageCount: number;
29 shapeCount: number;
30 bindingCount: number;
31 docSizeBytes: number;
32 lastUpdated: Timestamp;
33 },
34 ): BoardStats {
35 return {
36 pageCount: options.pageCount,
37 shapeCount: options.shapeCount,
38 bindingCount: options.bindingCount,
39 docSizeBytes: options.docSizeBytes,
40 lastUpdated: options.lastUpdated,
41 };
42 },
43
44 /**
45 * Format doc size in human-readable format (e.g., "1.2 KB", "3.4 MB")
46 */
47 formatDocSize(bytes: number): string {
48 if (bytes === 0) return "0 B";
49 if (bytes < 1024) return `${bytes} B`;
50 if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
51 return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
52 },
53};
54
55/**
56 * Determine pending migrations by comparing known migration list with applied migrations.
57 */
58export function getPendingMigrations(knownMigrationIds: string[], appliedMigrations: MigrationInfo[]): string[] {
59 const appliedIds = new Set(appliedMigrations.map((m) => m.id));
60 return knownMigrationIds.filter((id) => !appliedIds.has(id));
61}