Skip to content
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 39 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down Expand Up @@ -1381,6 +1381,38 @@ 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[]> {
const [rootHash, generation] = await this.#getRootHash(refresh);
// Get the raw text of the root entry

const lines = rootHash.split("\n");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the root hash should be just a string hash. Similar to other bulk actions, you should need to get the list of entries at that hash with:

const entries = await this.raw.getEntries(rootHash)

then to do a bulk purge you just need to remove the hashes from that entry set, and create a new entry with only the remaining ones, a. la. the last part of the bulk modify actions.

This way you can take advantage of the api parsing, rather than doing the raw line manipulation yourself, which is what it looks like you're doing.

This also does the v3 check (or if it doesn't, should, so some more code you can rely on general utilities.

Copy link
Contributor Author

@petero-dk petero-dk Jun 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not able to figure out how to get the entries list back to the root hash, is there a function that I missed? Which is why I settled for the raw manipulation, because it is just a matter of removing lines, not recalculating anything.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just look a few lines up:

    const [rootEntry, uploadRoot] = await this.raw.putEntries(
      "root",
      newEntries,
    );
    await uploadRoot;
    await this.#putRootHash(rootEntry.hash, generation);

the api may not be super clear, but the rootEntry has the new root hash, and uploadRoot is a promise that resolves when everything is actually uploaded. By splitting these up it allows more efficient async operations, but since you're only doing one upload, you might as well just await uploadRoot immediately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, now I realized why I did not go this route. the put entries also uploads the docSchema file, which the delete operation did not do, I just calls the putRootHash

I have made a new small method that creates the hashstring, to skip the putEntries so it functions just like the remarkable app. I will do some testing today on it.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leaving a comment inline, after you change

if (lines[0]?.startsWith("3")) {
// Remove lines that start with any hash + ":"
const hashSet = new Set(hashes.map(h => h + ":"));
const filtered = lines.filter(line => {
for (const prefix of hashSet) {
if (line.startsWith(prefix)) return false;
}
return true;
});
// Join the remaining lines
const newRootText = filtered.join("\n");
// Upload the new root entry
await this.#putRootHash(newRootText, generation);

const entries = await this.raw.getEntries(newRootText);
return entries.map(({ id, hash }) => ({ id, hash }));
} else {
// Not a v3 root, do nothing or throw
throw new Error("Root entry is not version 3, cannot purge.");
}
}

/** dump the raw cache */
dumpCache(): string {
return this.raw.dumpCache();
Expand Down