mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-07-23 04:52:58 +00:00
Reduce Community lint type-safety warnings
This commit is contained in:
@@ -189,7 +189,7 @@ export async function syncWithPeer(
|
||||
}
|
||||
const pushResult = await replicator.requestSynchroniseToPeer(targetPeer.peerId);
|
||||
if (!pushResult || pushResult.ok !== true) {
|
||||
const err = pushResult?.error;
|
||||
const err: unknown = pushResult && "error" in pushResult ? pushResult.error : undefined;
|
||||
throw err instanceof Error
|
||||
? err
|
||||
: LiveSyncError.fromError(err ?? "P2P sync failed while requesting remote sync");
|
||||
|
||||
@@ -8,11 +8,8 @@ import LevelDBAdapter from "pouchdb-adapter-leveldb";
|
||||
|
||||
import find from "pouchdb-find";
|
||||
import transform from "transform-pouch";
|
||||
//@ts-ignore
|
||||
import { findPathToLeaf } from "pouchdb-merge";
|
||||
//@ts-ignore
|
||||
import { findPathToLeaf, type RevisionTreeNode } from "pouchdb-merge";
|
||||
import { adapterFun } from "pouchdb-utils";
|
||||
//@ts-ignore
|
||||
import { createError, MISSING_DOC, UNKNOWN_ERROR } from "pouchdb-errors";
|
||||
import { mapAllTasksWithConcurrencyLimit, unwrapTaskResult } from "octagonal-wheels/concurrency/task";
|
||||
|
||||
@@ -28,8 +25,32 @@ type PurgeLogDocument = {
|
||||
purgeSeq: number;
|
||||
purges: Array<{ docId: string; rev: string; purgeSeq: number }>;
|
||||
};
|
||||
type PurgeMultiResultMap = Record<string, unknown>;
|
||||
|
||||
function appendPurgeSeqs(db: PouchDB.Database, docs: PurgeMultiParam[]) {
|
||||
interface PouchDBPrivateDatabase extends PouchDB.Database {
|
||||
adapter: string;
|
||||
purged_infos_limit: number;
|
||||
_getRevisionTree(
|
||||
documentId: string,
|
||||
callback: (error: Error | undefined, revisions?: RevisionTreeNode[]) => void
|
||||
): void;
|
||||
_purge(
|
||||
documentId: string,
|
||||
revisionPath: string[],
|
||||
callback: (error: Error | undefined, result?: PurgeMultiResult) => void
|
||||
): void;
|
||||
purgeMulti(documents: PurgeMultiParam[]): Promise<PurgeMultiResultMap>;
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null;
|
||||
}
|
||||
|
||||
function isSuccessfulPurge(value: unknown): value is PurgeMultiResult {
|
||||
return isRecord(value) && value.ok === true;
|
||||
}
|
||||
|
||||
function appendPurgeSeqs(db: PouchDBPrivateDatabase, docs: PurgeMultiParam[]) {
|
||||
return db
|
||||
.get<PurgeLogDocument>("_local/purges")
|
||||
.then(function (doc) {
|
||||
@@ -40,18 +61,16 @@ function appendPurgeSeqs(db: PouchDB.Database, docs: PurgeMultiParam[]) {
|
||||
rev: rev$$1,
|
||||
purgeSeq,
|
||||
});
|
||||
//@ts-ignore : missing type def
|
||||
if (doc.purges.length > db.purged_infos_limit) {
|
||||
//@ts-ignore : missing type def
|
||||
doc.purges.splice(0, doc.purges.length - db.purged_infos_limit);
|
||||
}
|
||||
doc.purgeSeq = purgeSeq;
|
||||
}
|
||||
return doc;
|
||||
})
|
||||
.catch(function (err) {
|
||||
if (err.status !== 404) {
|
||||
throw err;
|
||||
.catch(function (error: unknown) {
|
||||
if (!isRecord(error) || error.status !== 404) {
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
_id: "_local/purges",
|
||||
@@ -71,68 +90,76 @@ function appendPurgeSeqs(db: PouchDB.Database, docs: PurgeMultiParam[]) {
|
||||
/**
|
||||
* purge multiple documents at once.
|
||||
*/
|
||||
PouchDB.prototype.purgeMulti = adapterFun(
|
||||
const pouchDBPrototype = (PouchDB as typeof PouchDB & { prototype: PouchDBPrivateDatabase }).prototype;
|
||||
|
||||
pouchDBPrototype.purgeMulti = adapterFun<PouchDBPrivateDatabase, [documents: PurgeMultiParam[]], PurgeMultiResultMap>(
|
||||
"_purgeMulti",
|
||||
function (
|
||||
this: PouchDBPrivateDatabase,
|
||||
docs: PurgeMultiParam[],
|
||||
callback: (
|
||||
error: Error,
|
||||
result?: {
|
||||
[x: string]: PurgeMultiResult | Error;
|
||||
}
|
||||
) => void
|
||||
callback: (error?: Error, result?: PurgeMultiResultMap) => void
|
||||
) {
|
||||
//@ts-ignore
|
||||
if (typeof this._purge === "undefined") {
|
||||
return callback(
|
||||
//@ts-ignore: this ts-ignore might be hiding a `this` bug where we don't have "this" conext.
|
||||
createError(UNKNOWN_ERROR, "Purge is not implemented in the " + this.adapter + " adapter.")
|
||||
);
|
||||
}
|
||||
//@ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias -- The adapter task callbacks must retain this PouchDB instance.
|
||||
const self = this;
|
||||
const tasks = docs.map(
|
||||
(param) => () =>
|
||||
new Promise<[PurgeMultiParam, PurgeMultiResult | Error]>((res, rej) => {
|
||||
new Promise<[PurgeMultiParam, unknown]>((res) => {
|
||||
const [docId, rev$$1] = param;
|
||||
self._getRevisionTree(docId, (error: Error, revs: string[]) => {
|
||||
self._getRevisionTree(docId, (error, revs) => {
|
||||
if (error) {
|
||||
return res([param, error]);
|
||||
}
|
||||
if (!revs) {
|
||||
return res([param, createError(MISSING_DOC)]);
|
||||
}
|
||||
let path;
|
||||
let path: string[];
|
||||
try {
|
||||
path = findPathToLeaf(revs, rev$$1);
|
||||
} catch (error) {
|
||||
//@ts-ignore
|
||||
return res([param, error.message || error]);
|
||||
} catch (caught: unknown) {
|
||||
const failure = caught instanceof Error && caught.message ? caught.message : caught;
|
||||
return res([param, failure]);
|
||||
}
|
||||
self._purge(docId, path, (error: Error, result: PurgeMultiResult) => {
|
||||
self._purge(docId, path, (error, result) => {
|
||||
if (error) {
|
||||
return res([param, error]);
|
||||
} else {
|
||||
return res([param, result]);
|
||||
}
|
||||
return res([param, result]);
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
(async () => {
|
||||
const ret = await mapAllTasksWithConcurrencyLimit(1, tasks);
|
||||
const retAll = ret.map((e) => unwrapTaskResult(e)) as [PurgeMultiParam, PurgeMultiResult | Error][];
|
||||
await appendPurgeSeqs(
|
||||
self,
|
||||
retAll.filter((e) => "ok" in e[1]).map((e) => e[0])
|
||||
);
|
||||
const result = Object.fromEntries(retAll.map((e) => [e[0][0], e[1]]));
|
||||
const retAll: Array<[PurgeMultiParam, unknown]> = [];
|
||||
for (const entry of ret) {
|
||||
const outcome = unwrapTaskResult(entry);
|
||||
if (outcome instanceof Error) {
|
||||
throw outcome;
|
||||
}
|
||||
retAll.push(outcome);
|
||||
}
|
||||
const successfullyPurged: PurgeMultiParam[] = [];
|
||||
const resultEntries: Array<[string, unknown]> = [];
|
||||
for (const [document, outcome] of retAll) {
|
||||
if (isSuccessfulPurge(outcome)) {
|
||||
successfullyPurged.push(document);
|
||||
}
|
||||
resultEntries.push([document[0], outcome]);
|
||||
}
|
||||
await appendPurgeSeqs(self, successfullyPurged);
|
||||
const result: PurgeMultiResultMap = Object.fromEntries(resultEntries);
|
||||
return result;
|
||||
})()
|
||||
//@ts-ignore
|
||||
.then((result) => callback(undefined, result))
|
||||
.catch((error) => callback(error));
|
||||
.catch((caught: unknown) => {
|
||||
const error = caught instanceof Error ? caught : new Error(String(caught));
|
||||
callback(error);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
declare module "pouchdb-merge" {
|
||||
export interface RevisionTreeNode {
|
||||
pos: number;
|
||||
ids: [revision: string, metadata: Record<string, unknown>, branches: RevisionTreeNode["ids"][]];
|
||||
}
|
||||
|
||||
export function findPathToLeaf(revisions: RevisionTreeNode[], targetRevision: string): string[];
|
||||
}
|
||||
|
||||
declare module "pouchdb-utils" {
|
||||
export function adapterFun<TThis, TArguments extends unknown[], TResult>(
|
||||
name: string,
|
||||
callback: (this: TThis, ...args: [...TArguments, callback: (error?: Error, result?: TResult) => void]) => void
|
||||
): (this: TThis, ...args: TArguments) => Promise<TResult>;
|
||||
}
|
||||
|
||||
declare module "pouchdb-errors" {
|
||||
export const MISSING_DOC: unknown;
|
||||
export const UNKNOWN_ERROR: unknown;
|
||||
export function createError(error: unknown, reason?: string): Error;
|
||||
}
|
||||
@@ -82,6 +82,17 @@ function deserializeFromNodeKV(value: unknown): unknown {
|
||||
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, deserializeFromNodeKV(v)]));
|
||||
}
|
||||
|
||||
function asKeyString(key: unknown): string {
|
||||
if (typeof key === "string") {
|
||||
return key;
|
||||
}
|
||||
const serialised = JSON.stringify(key);
|
||||
if (typeof serialised !== "string") {
|
||||
throw new TypeError("The IndexedDB key could not be serialised");
|
||||
}
|
||||
return serialised;
|
||||
}
|
||||
|
||||
class NodeFileKeyValueDatabase implements KeyValueDatabase {
|
||||
private filePath: string;
|
||||
private data = new Map<string, unknown>();
|
||||
@@ -91,13 +102,6 @@ class NodeFileKeyValueDatabase implements KeyValueDatabase {
|
||||
this.load();
|
||||
}
|
||||
|
||||
private asKeyString(key: IDBValidKey): string {
|
||||
if (typeof key === "string") {
|
||||
return key;
|
||||
}
|
||||
return JSON.stringify(key);
|
||||
}
|
||||
|
||||
private load() {
|
||||
try {
|
||||
const loaded = JSON.parse(nodeFs.readFileSync(this.filePath, "utf-8")) as Record<string, unknown>;
|
||||
@@ -116,17 +120,17 @@ class NodeFileKeyValueDatabase implements KeyValueDatabase {
|
||||
}
|
||||
|
||||
async get<T>(key: IDBValidKey): Promise<T> {
|
||||
return this.data.get(this.asKeyString(key)) as T;
|
||||
return this.data.get(asKeyString(key)) as T;
|
||||
}
|
||||
|
||||
async set<T>(key: IDBValidKey, value: T): Promise<IDBValidKey> {
|
||||
this.data.set(this.asKeyString(key), value);
|
||||
this.data.set(asKeyString(key), value);
|
||||
this.flush();
|
||||
return key;
|
||||
}
|
||||
|
||||
async del(key: IDBValidKey): Promise<void> {
|
||||
this.data.delete(this.asKeyString(key));
|
||||
this.data.delete(asKeyString(key));
|
||||
this.flush();
|
||||
}
|
||||
|
||||
@@ -144,11 +148,12 @@ class NodeFileKeyValueDatabase implements KeyValueDatabase {
|
||||
let filtered = allKeys;
|
||||
if (typeof query !== "undefined") {
|
||||
if (this.isIDBKeyRangeLike(query)) {
|
||||
const lower = query.lower?.toString() ?? "";
|
||||
const upper = query.upper?.toString() ?? "\uffff";
|
||||
const lower = query.lower === undefined ? "" : String(query.lower);
|
||||
const upper = query.upper === undefined ? "\uffff" : String(query.upper);
|
||||
filtered = filtered.filter((key) => key >= lower && key <= upper);
|
||||
} else {
|
||||
const exact = query.toString();
|
||||
const exactValue: unknown = query;
|
||||
const exact = String(exactValue);
|
||||
filtered = filtered.filter((key) => key === exact);
|
||||
}
|
||||
}
|
||||
@@ -272,7 +277,15 @@ export class NodeKeyValueDBService<T extends ServiceContext = ServiceContext>
|
||||
await getDB().del(`${prefix}${key}`);
|
||||
},
|
||||
keys: async (from: string | undefined, to: string | undefined, count?: number): Promise<string[]> => {
|
||||
const allKeys = (await getDB().keys(undefined, count)).map((e) => e.toString());
|
||||
const rawKeys: unknown = await getDB().keys(undefined, count);
|
||||
if (!Array.isArray(rawKeys)) {
|
||||
throw new TypeError("The key-value database returned an invalid key list");
|
||||
}
|
||||
const keyList: unknown[] = rawKeys;
|
||||
const allKeys: string[] = [];
|
||||
for (const key of keyList) {
|
||||
allKeys.push(String(key));
|
||||
}
|
||||
const lower = `${prefix}${from ?? ""}`;
|
||||
const upper = `${prefix}${to ?? "\uffff"}`;
|
||||
return allKeys
|
||||
|
||||
@@ -4,8 +4,17 @@ import { fileURLToPath, fs, isBuiltin, path } from "@vrtmrz/livesync-commonlib/n
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const resolve = (...args: string[]) => path.resolve(...args).replace(/\\/g, "/");
|
||||
const repoRoot = path.resolve(__dirname, "../../..");
|
||||
const packageJson = JSON.parse(fs.readFileSync(path.resolve(repoRoot, "package.json"), "utf-8"));
|
||||
const manifestJson = JSON.parse(fs.readFileSync(path.resolve(repoRoot, "manifest.json"), "utf-8"));
|
||||
|
||||
function readVersion(filePath: string): string | undefined {
|
||||
const parsed: unknown = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
||||
if (typeof parsed !== "object" || parsed === null || !("version" in parsed)) {
|
||||
return undefined;
|
||||
}
|
||||
return typeof parsed.version === "string" ? parsed.version : undefined;
|
||||
}
|
||||
|
||||
const packageVersion = readVersion(path.resolve(repoRoot, "package.json"));
|
||||
const manifestVersion = readVersion(path.resolve(repoRoot, "manifest.json"));
|
||||
// https://vite.dev/config/
|
||||
const defaultExternal = [
|
||||
"obsidian",
|
||||
@@ -113,7 +122,7 @@ export default defineConfig({
|
||||
global: "globalThis",
|
||||
nonInteractive: "true",
|
||||
// localStorage: "undefined", // Prevent usage of localStorage in the CLI environment
|
||||
MANIFEST_VERSION: JSON.stringify(process.env.MANIFEST_VERSION || manifestJson.version || "0.0.0"),
|
||||
PACKAGE_VERSION: JSON.stringify(process.env.PACKAGE_VERSION || packageJson.version || "0.0.0"),
|
||||
MANIFEST_VERSION: JSON.stringify(process.env.MANIFEST_VERSION || manifestVersion || "0.0.0"),
|
||||
PACKAGE_VERSION: JSON.stringify(process.env.PACKAGE_VERSION || packageVersion || "0.0.0"),
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user