import fs from "node:fs"; import path from "node:path"; import { indexedDB as fakeIndexedDB } from "fake-indexeddb"; import { withFileLock } from "openclaw/plugin-sdk/file-lock"; import { MATRIX_IDB_SNAPSHOT_LOCK_OPTIONS } from "./idb-persistence-lock.js"; import { LogService } from "./logger.js";
// Advisory lock options for IDB snapshot file access. Without locking, the // gateway's periodic 60-second persist cycle and CLI crypto commands (e.g. // `openclaw matrix verify bootstrap`) can corrupt each other's state. // Use a longer stale window than the generic 30s default because snapshot // restore and large crypto-store dumps can legitimately hold the lock for // longer, and reclaiming a live lock would reintroduce concurrent corruption.
type IdbStoreSnapshot = {
name: string;
keyPath: IDBObjectStoreParameters["keyPath"];
autoIncrement: boolean;
indexes: { name: string; keyPath: string | string[]; multiEntry: boolean; unique: boolean }[];
records: { key: IDBValidKey; value: unknown }[];
};
function isValidIdbIndexSnapshot(value: unknown): value is IdbStoreSnapshot["indexes"][number] { if (!value || typeof value !== "object") { returnfalse;
} const candidate = value as Partial<IdbStoreSnapshot["indexes"][number]>; return ( typeof candidate.name === "string" &&
(typeof candidate.keyPath === "string" ||
(Array.isArray(candidate.keyPath) &&
candidate.keyPath.every((entry) => typeof entry === "string"))) && typeof candidate.multiEntry === "boolean" && typeof candidate.unique === "boolean"
);
}
function isValidIdbRecordSnapshot(value: unknown): value is IdbStoreSnapshot["records"][number] { if (!value || typeof value !== "object") { returnfalse;
} return"key" in value && "value" in value;
}
function isValidIdbStoreSnapshot(value: unknown): value is IdbStoreSnapshot { if (!value || typeof value !== "object") { returnfalse;
} const candidate = value as Partial<IdbStoreSnapshot>; const validKeyPath =
candidate.keyPath === null || typeof candidate.keyPath === "string" ||
(Array.isArray(candidate.keyPath) &&
candidate.keyPath.every((entry) => typeof entry === "string")); return ( typeof candidate.name === "string" &&
validKeyPath && typeof candidate.autoIncrement === "boolean" &&
Array.isArray(candidate.indexes) &&
candidate.indexes.every((entry) => isValidIdbIndexSnapshot(entry)) &&
Array.isArray(candidate.records) &&
candidate.records.every((entry) => isValidIdbRecordSnapshot(entry))
);
}
function isValidIdbDatabaseSnapshot(value: unknown): value is IdbDatabaseSnapshot { if (!value || typeof value !== "object") { returnfalse;
} const candidate = value as Partial<IdbDatabaseSnapshot>; return ( typeof candidate.name === "string" && typeof candidate.version === "number" &&
Number.isFinite(candidate.version) &&
candidate.version > 0 &&
Array.isArray(candidate.stores) &&
candidate.stores.every((entry) => isValidIdbStoreSnapshot(entry))
);
}
function parseSnapshotPayload(data: string): IdbDatabaseSnapshot[] | null { const parsed = JSON.parse(data) as unknown; if (!Array.isArray(parsed) || parsed.length === 0) { returnnull;
} if (!parsed.every((entry) => isValidIdbDatabaseSnapshot(entry))) { thrownew Error("Malformed IndexedDB snapshot payload");
} return parsed;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.