rmapi-js 14.1.0 → 14.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +57 -0
- package/dist/index.js +163 -18
- package/dist/rmapi-js.esm.min.js +14 -14
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -667,6 +667,28 @@ declare class Remarkable {
|
|
|
667
667
|
* @returns a reference to the deleted entry, with its new hash
|
|
668
668
|
*/
|
|
669
669
|
delete(ref: ItemRef, refresh?: boolean): Promise<ItemRef>;
|
|
670
|
+
/**
|
|
671
|
+
* permanently delete an entry
|
|
672
|
+
*
|
|
673
|
+
* Unlike {@link delete | `delete`}, which moves an entry to the trash where
|
|
674
|
+
* the device can still restore it, this drops the entry from the account
|
|
675
|
+
* outright. Its files stay in the cloud, but nothing points at them anymore
|
|
676
|
+
* and nothing brings the entry back.
|
|
677
|
+
*
|
|
678
|
+
* Only the entry named goes: purging a folder leaves everything inside it
|
|
679
|
+
* pointing at a parent that's no longer there. Those entries stay in the
|
|
680
|
+
* account and {@link listItems | `listItems`} still returns them, but no
|
|
681
|
+
* folder holds them, so nothing browsing the tree will find them. Purge the
|
|
682
|
+
* contents first, or use {@link purgeTrash | `purgeTrash`}, which takes the
|
|
683
|
+
* whole tree.
|
|
684
|
+
*
|
|
685
|
+
* @example
|
|
686
|
+
* ```ts
|
|
687
|
+
* await api.purge(file);
|
|
688
|
+
* ```
|
|
689
|
+
* @param ref - a reference to the entry to purge
|
|
690
|
+
*/
|
|
691
|
+
purge(ref: ItemRef, refresh?: boolean): Promise<void>;
|
|
670
692
|
/**
|
|
671
693
|
* rename an entry
|
|
672
694
|
*
|
|
@@ -716,6 +738,41 @@ declare class Remarkable {
|
|
|
716
738
|
* @returns references to the deleted entries, each with its new hash
|
|
717
739
|
*/
|
|
718
740
|
bulkDelete(refs: readonly ItemRef[], refresh?: boolean): Promise<ItemRef[]>;
|
|
741
|
+
/**
|
|
742
|
+
* permanently delete many entries
|
|
743
|
+
*
|
|
744
|
+
* The bulk form of {@link purge | `purge`}, done in a single root write.
|
|
745
|
+
*
|
|
746
|
+
* @example
|
|
747
|
+
* ```ts
|
|
748
|
+
* await api.bulkPurge([file]);
|
|
749
|
+
* ```
|
|
750
|
+
*
|
|
751
|
+
* @param refs - references to the entries to purge
|
|
752
|
+
*/
|
|
753
|
+
bulkPurge(refs: readonly ItemRef[], refresh?: boolean): Promise<void>;
|
|
754
|
+
/**
|
|
755
|
+
* permanently delete everything in the trash
|
|
756
|
+
*
|
|
757
|
+
* Trashing a folder doesn't touch what's inside it — those entries keep
|
|
758
|
+
* naming the folder as their parent, which is what lets the device restore
|
|
759
|
+
* them together — so the trash holds the whole tree hanging off it, not just
|
|
760
|
+
* the entries whose parent is "trash". This purges all of it in one root
|
|
761
|
+
* write.
|
|
762
|
+
*
|
|
763
|
+
* @example
|
|
764
|
+
* ```ts
|
|
765
|
+
* await api.purgeTrash();
|
|
766
|
+
* ```
|
|
767
|
+
*
|
|
768
|
+
* @remarks
|
|
769
|
+
* Finding that tree means reading every item's metadata, so this costs about
|
|
770
|
+
* as much as {@link listItems | `listItems`}.
|
|
771
|
+
*
|
|
772
|
+
* @param refresh - if true, refresh the root hash before purging
|
|
773
|
+
* @returns references to the entries that were purged
|
|
774
|
+
*/
|
|
775
|
+
purgeTrash(refresh?: boolean): Promise<ItemRef[]>;
|
|
719
776
|
/**
|
|
720
777
|
* listen for sync notifications
|
|
721
778
|
*
|
package/dist/index.js
CHANGED
|
@@ -407,26 +407,35 @@ class Remarkable {
|
|
|
407
407
|
hash: rootHash,
|
|
408
408
|
});
|
|
409
409
|
entries.push(entry);
|
|
410
|
-
|
|
411
|
-
{
|
|
412
|
-
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
413
|
-
try {
|
|
414
|
-
const rootEntry = __addDisposableResource(env_2, await this.raw.putEntries(ROOT_LIST, entries, 4), true);
|
|
415
|
-
newRoot = rootEntry.hash;
|
|
416
|
-
}
|
|
417
|
-
catch (e_2) {
|
|
418
|
-
env_2.error = e_2;
|
|
419
|
-
env_2.hasError = true;
|
|
420
|
-
}
|
|
421
|
-
finally {
|
|
422
|
-
const result_2 = __disposeResources(env_2);
|
|
423
|
-
if (result_2)
|
|
424
|
-
await result_2;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
await this.#putRootHash(newRoot, generation);
|
|
410
|
+
await this.#writeRoot(entries, generation);
|
|
428
411
|
});
|
|
429
412
|
}
|
|
413
|
+
/**
|
|
414
|
+
* upload a new root index and point the account at it
|
|
415
|
+
*
|
|
416
|
+
* Every blob the index names must already be uploaded, since the root hash
|
|
417
|
+
* lands as soon as the index does.
|
|
418
|
+
*/
|
|
419
|
+
async #writeRoot(entries, generation) {
|
|
420
|
+
let newRoot;
|
|
421
|
+
{
|
|
422
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
423
|
+
try {
|
|
424
|
+
const rootEntry = __addDisposableResource(env_2, await this.raw.putEntries(ROOT_LIST, entries, 4), true);
|
|
425
|
+
newRoot = rootEntry.hash;
|
|
426
|
+
}
|
|
427
|
+
catch (e_2) {
|
|
428
|
+
env_2.error = e_2;
|
|
429
|
+
env_2.hasError = true;
|
|
430
|
+
}
|
|
431
|
+
finally {
|
|
432
|
+
const result_2 = __disposeResources(env_2);
|
|
433
|
+
if (result_2)
|
|
434
|
+
await result_2;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
await this.#putRootHash(newRoot, generation);
|
|
438
|
+
}
|
|
430
439
|
async #authedFetch(url, { body, method = "POST", headers = {}, }) {
|
|
431
440
|
// the root PUT is a compare-and-set; retrying a lost-but-applied response
|
|
432
441
|
// would resurface as a false generation conflict and be double-applied by
|
|
@@ -1517,6 +1526,30 @@ class Remarkable {
|
|
|
1517
1526
|
async delete(ref, refresh = false) {
|
|
1518
1527
|
return await this.move(ref, TRASH_ID, refresh);
|
|
1519
1528
|
}
|
|
1529
|
+
/**
|
|
1530
|
+
* permanently delete an entry
|
|
1531
|
+
*
|
|
1532
|
+
* Unlike {@link delete | `delete`}, which moves an entry to the trash where
|
|
1533
|
+
* the device can still restore it, this drops the entry from the account
|
|
1534
|
+
* outright. Its files stay in the cloud, but nothing points at them anymore
|
|
1535
|
+
* and nothing brings the entry back.
|
|
1536
|
+
*
|
|
1537
|
+
* Only the entry named goes: purging a folder leaves everything inside it
|
|
1538
|
+
* pointing at a parent that's no longer there. Those entries stay in the
|
|
1539
|
+
* account and {@link listItems | `listItems`} still returns them, but no
|
|
1540
|
+
* folder holds them, so nothing browsing the tree will find them. Purge the
|
|
1541
|
+
* contents first, or use {@link purgeTrash | `purgeTrash`}, which takes the
|
|
1542
|
+
* whole tree.
|
|
1543
|
+
*
|
|
1544
|
+
* @example
|
|
1545
|
+
* ```ts
|
|
1546
|
+
* await api.purge(file);
|
|
1547
|
+
* ```
|
|
1548
|
+
* @param ref - a reference to the entry to purge
|
|
1549
|
+
*/
|
|
1550
|
+
async purge(ref, refresh = false) {
|
|
1551
|
+
await this.bulkPurge([ref], refresh);
|
|
1552
|
+
}
|
|
1520
1553
|
/**
|
|
1521
1554
|
* rename an entry
|
|
1522
1555
|
*
|
|
@@ -1631,6 +1664,118 @@ class Remarkable {
|
|
|
1631
1664
|
async bulkDelete(refs, refresh = false) {
|
|
1632
1665
|
return await this.bulkMove(refs, TRASH_ID, refresh);
|
|
1633
1666
|
}
|
|
1667
|
+
/**
|
|
1668
|
+
* permanently delete many entries
|
|
1669
|
+
*
|
|
1670
|
+
* The bulk form of {@link purge | `purge`}, done in a single root write.
|
|
1671
|
+
*
|
|
1672
|
+
* @example
|
|
1673
|
+
* ```ts
|
|
1674
|
+
* await api.bulkPurge([file]);
|
|
1675
|
+
* ```
|
|
1676
|
+
*
|
|
1677
|
+
* @param refs - references to the entries to purge
|
|
1678
|
+
*/
|
|
1679
|
+
async bulkPurge(refs, refresh = false) {
|
|
1680
|
+
if (!refs.length) {
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
await this.#withRetry(async () => {
|
|
1684
|
+
const [rootHash, generation] = await this.#getRootHash(refresh);
|
|
1685
|
+
const { entries } = await this.raw.getEntries({
|
|
1686
|
+
id: ROOT_LIST,
|
|
1687
|
+
hash: rootHash,
|
|
1688
|
+
});
|
|
1689
|
+
const wanted = new Set(refs.map((ref) => `${ref.id}\0${ref.hash}`));
|
|
1690
|
+
const found = new Set();
|
|
1691
|
+
const newEntries = [];
|
|
1692
|
+
for (const entry of entries) {
|
|
1693
|
+
const key = `${entry.id}\0${entry.hash}`;
|
|
1694
|
+
if (wanted.has(key)) {
|
|
1695
|
+
found.add(key);
|
|
1696
|
+
}
|
|
1697
|
+
else {
|
|
1698
|
+
newEntries.push(entry);
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
for (const ref of refs) {
|
|
1702
|
+
if (!found.has(`${ref.id}\0${ref.hash}`)) {
|
|
1703
|
+
throw new HashNotFoundError(ref.hash);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
await this.#writeRoot(newEntries, generation);
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
/**
|
|
1710
|
+
* permanently delete everything in the trash
|
|
1711
|
+
*
|
|
1712
|
+
* Trashing a folder doesn't touch what's inside it — those entries keep
|
|
1713
|
+
* naming the folder as their parent, which is what lets the device restore
|
|
1714
|
+
* them together — so the trash holds the whole tree hanging off it, not just
|
|
1715
|
+
* the entries whose parent is "trash". This purges all of it in one root
|
|
1716
|
+
* write.
|
|
1717
|
+
*
|
|
1718
|
+
* @example
|
|
1719
|
+
* ```ts
|
|
1720
|
+
* await api.purgeTrash();
|
|
1721
|
+
* ```
|
|
1722
|
+
*
|
|
1723
|
+
* @remarks
|
|
1724
|
+
* Finding that tree means reading every item's metadata, so this costs about
|
|
1725
|
+
* as much as {@link listItems | `listItems`}.
|
|
1726
|
+
*
|
|
1727
|
+
* @param refresh - if true, refresh the root hash before purging
|
|
1728
|
+
* @returns references to the entries that were purged
|
|
1729
|
+
*/
|
|
1730
|
+
async purgeTrash(refresh = false) {
|
|
1731
|
+
return await this.#withRetry(async () => {
|
|
1732
|
+
const [rootHash, generation] = await this.#getRootHash(refresh);
|
|
1733
|
+
const { entries } = await this.raw.getEntries({
|
|
1734
|
+
id: ROOT_LIST,
|
|
1735
|
+
hash: rootHash,
|
|
1736
|
+
});
|
|
1737
|
+
const parents = await Promise.all(entries.map(async (entry) => (await this.getMetadata(entry)).parent));
|
|
1738
|
+
const children = new Map();
|
|
1739
|
+
for (const [ind, entry] of entries.entries()) {
|
|
1740
|
+
const siblings = children.get(parents[ind]);
|
|
1741
|
+
if (siblings === undefined) {
|
|
1742
|
+
children.set(parents[ind], [entry]);
|
|
1743
|
+
}
|
|
1744
|
+
else {
|
|
1745
|
+
siblings.push(entry);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
const trashed = new Set();
|
|
1749
|
+
let frontier = children.get(TRASH_ID) ?? [];
|
|
1750
|
+
while (frontier.length) {
|
|
1751
|
+
const next = [];
|
|
1752
|
+
for (const { id } of frontier) {
|
|
1753
|
+
// skipping what we've already seen also ends a parent cycle, which
|
|
1754
|
+
// the account shouldn't have but which would loop here forever
|
|
1755
|
+
if (!trashed.has(id)) {
|
|
1756
|
+
trashed.add(id);
|
|
1757
|
+
next.push(...(children.get(id) ?? []));
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
frontier = next;
|
|
1761
|
+
}
|
|
1762
|
+
if (!trashed.size) {
|
|
1763
|
+
return [];
|
|
1764
|
+
}
|
|
1765
|
+
const purged = [];
|
|
1766
|
+
const newEntries = [];
|
|
1767
|
+
for (const entry of entries) {
|
|
1768
|
+
if (trashed.has(entry.id)) {
|
|
1769
|
+
purged.push({ id: entry.id, hash: entry.hash });
|
|
1770
|
+
}
|
|
1771
|
+
else {
|
|
1772
|
+
newEntries.push(entry);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
await this.#writeRoot(newEntries, generation);
|
|
1776
|
+
return purged;
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1634
1779
|
/**
|
|
1635
1780
|
* listen for sync notifications
|
|
1636
1781
|
*
|