diff --git a/src/index.ts b/src/index.ts index cb07d16..ab82b86 100644 --- a/src/index.ts +++ b/src/index.ts @@ -226,13 +226,13 @@ export interface RegisterOptions { * Using an improper one will results in the registration being rejected. */ deviceDesc?: - | "desktop-windows" - | "desktop-macos" - | "desktop-linux" - | "mobile-android" - | "mobile-ios" - | "browser-chrome" - | "remarkable"; + | "desktop-windows" + | "desktop-macos" + | "desktop-linux" + | "mobile-android" + | "mobile-ios" + | "browser-chrome" + | "remarkable"; /** * the unique id of this device * @@ -634,6 +634,18 @@ export interface RemarkableApi { */ delete(hash: string, refresh?: boolean): Promise; + + /** + * permanently delete an entry from all devices connected to the account + * + * @example + * ```ts + * await api.purge(file.hash); + * ``` + * @param hash - the hash of the entry to delete + */ + purge(hash: string, refresh?: boolean): Promise; + /** * rename an entry * @@ -694,6 +706,21 @@ export interface RemarkableApi { refresh?: boolean, ): Promise; + /** + * delete many entries permanently from all devices connected to the account + * + * @example + * ```ts + * await api.bulkPurge([file.hash]); + * ``` + * + * @param hashes - the hashes of the entries to delete + */ + bulkPurge( + hashes: readonly string[], + refresh?: boolean, + ): Promise<[SimpleEntry[], string]>; + /** * get the current cache value as a string * @@ -1308,6 +1335,11 @@ class Remarkable implements RemarkableApi { async delete(hash: string, refresh: boolean = false): Promise { return await this.move(hash, "trash", refresh); } + + /** permanently delete an entry */ + async purge(hash: string, refresh: boolean = false): Promise { + await this.bulkPurge([hash], refresh); + } /** rename an entry */ async rename( @@ -1381,6 +1413,26 @@ class Remarkable implements RemarkableApi { return await this.bulkMove(hashes, "trash", refresh); } + /** permanent delete many hashes */ + async bulkPurge( + hashes: readonly string[], + refresh: boolean = false, + ): Promise<[SimpleEntry[], string]> { + const [rootHash, generation] = await this.#getRootHash(refresh); + // Get the raw text of the root entry + const entries = await this.raw.getEntries(rootHash) + const newEntries = entries.filter( + (entry) => !hashes.includes(entry.hash)); + // If we didn't delete anything, just return the current root + if (newEntries.length < entries.length) { + const hash = this.raw.makeListHash(newEntries); + await this.#putRootHash(hash, generation); + return [newEntries, hash]; + } + return [newEntries, rootHash]; + + } + /** dump the raw cache */ dumpCache(): string { return this.raw.dumpCache(); diff --git a/src/raw.ts b/src/raw.ts index 7a5d4a2..d4cf736 100644 --- a/src/raw.ts +++ b/src/raw.ts @@ -840,6 +840,12 @@ export interface RawRemarkableApi { metadata: Metadata, ): Promise<[RawFileEntry, Promise]>; + /** + * create a list hash from a set of entries + * @param entries - the entries to hash + */ + makeListHash(entries: RawEntry[]): string; + /** * put a set of entries to make an entry list file * @@ -1145,6 +1151,14 @@ export class RawRemarkable implements RawRemarkableApi { } } + makeListHash(entries: RawEntry[]): string { + const records = ["3\n"]; + for (const { hash, type, id, subfiles, size } of entries) { + records.push(`${hash}:${type}:${id}:${subfiles}:${size}\n`); + } + return records.join(""); + } + async putEntries( id: string, entries: RawEntry[], @@ -1161,10 +1175,8 @@ export class RawRemarkable implements RawRemarkableApi { const hash = await digest(hashBuff); const size = entries.reduce((acc, ent) => acc + ent.size, 0); - const records = ["3\n"]; - for (const { hash, type, id, subfiles, size } of entries) { - records.push(`${hash}:${type}:${id}:${subfiles}:${size}\n`); - } + const listHash = this.makeListHash(entries); + const res: RawListEntry = { id, hash, @@ -1176,7 +1188,7 @@ export class RawRemarkable implements RawRemarkableApi { return [ res, // NOTE when monitoring requests, this had the extension .docSchema appended, but I'm not entirely sure why - this.#putFile(hash, `${id}.docSchema`, enc.encode(records.join(""))), + this.#putFile(hash, `${id}.docSchema`, enc.encode(listHash)), ]; }