Skip to content

Commit 6f2a9b3

Browse files
authored
update convenience apis (#18)
1 parent df13a66 commit 6f2a9b3

File tree

3 files changed

+202
-6
lines changed

3 files changed

+202
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Convenience high level apis for `pinned`, `updateDocument`, `updateCollection`, and `updateTemplate`.
13+
1014
## [8.1.1] - 2025-05-01
1115

1216
### Changed

src/index.spec.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,43 @@ ${epubHash}:0:doc.epub:0:1
534534
expect(res.hash).toHaveLength(64);
535535
});
536536

537+
test("#stared()", async () => {
538+
const moveHash = repHash("1");
539+
const oldMeta: Metadata = {
540+
lastModified: "",
541+
visibleName: "test",
542+
parent: "",
543+
pinned: false,
544+
type: "DocumentType",
545+
};
546+
547+
mockFetch(
548+
emptyResponse(),
549+
jsonResponse({
550+
hash: repHash("0"),
551+
generation: 0,
552+
schemaVersion: 3,
553+
}), // root hash
554+
textResponse(`3\n${moveHash}:80000000:fake_id:2:123\n`), // root entries
555+
textResponse(
556+
`3\n${repHash("2")}:0:fake_id.metadata:0:1\n${repHash("3")}:0:fake_id.content:0:122\n`,
557+
), // item entries
558+
jsonResponse(oldMeta), // get metadata
559+
emptyResponse(), // put metadata
560+
emptyResponse(), // put entries
561+
emptyResponse(), // put root entries
562+
jsonResponse({
563+
hash: repHash("1"),
564+
generation: 1,
565+
}), // root hash
566+
);
567+
568+
const api = await remarkable("");
569+
const res = await api.stared(moveHash, true);
570+
571+
expect(res.hash).toHaveLength(64);
572+
});
573+
537574
test("#move()", async () => {
538575
const moveHash = repHash("1");
539576
const oldMeta: Metadata = {

src/index.ts

Lines changed: 161 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,6 +1352,57 @@ export interface RemarkableApi {
13521352
opts?: UploadOptions,
13531353
): Promise<SimpleEntry>;
13541354

1355+
/**
1356+
* update content metadata for a document
1357+
*
1358+
* @example
1359+
* ```ts
1360+
* await api.updateDocument(doc.hash, { textAlignment: "left" });
1361+
* ```
1362+
*
1363+
* @param hash - the hash of the file to update
1364+
* @param content - the fields of content to update
1365+
*/
1366+
updateDocument(
1367+
hash: string,
1368+
content: Partial<DocumentContent>,
1369+
refresh?: boolean,
1370+
): Promise<HashEntry>;
1371+
1372+
/**
1373+
* update content metadata for a collection
1374+
*
1375+
* @example
1376+
* ```ts
1377+
* await api.updateCollection(doc.hash, { textAlignment: "left" });
1378+
* ```
1379+
*
1380+
* @param hash - the hash of the file to update
1381+
* @param content - the fields of content to update
1382+
*/
1383+
updateCollection(
1384+
hash: string,
1385+
content: Partial<CollectionContent>,
1386+
refresh?: boolean,
1387+
): Promise<HashEntry>;
1388+
1389+
/**
1390+
* update content metadata for a template
1391+
*
1392+
* @example
1393+
* ```ts
1394+
* await api.updateTemplate(doc.hash, { textAlignment: "left" });
1395+
* ```
1396+
*
1397+
* @param hash - the hash of the file to update
1398+
* @param content - the fields of content to update
1399+
*/
1400+
updateTemplate(
1401+
hash: string,
1402+
content: Partial<TemplateContent>,
1403+
refresh?: boolean,
1404+
): Promise<HashEntry>;
1405+
13551406
/**
13561407
* move an entry
13571408
*
@@ -1392,6 +1443,18 @@ export interface RemarkableApi {
13921443
refresh?: boolean,
13931444
): Promise<HashEntry>;
13941445

1446+
/**
1447+
* set if an entry is stared
1448+
*
1449+
* @example
1450+
* ```ts
1451+
* await api.stared(file.hash, true);
1452+
* ```
1453+
* @param hash - the hash of the entry to rename
1454+
* @param stared - whether the entry should be stared or not
1455+
*/
1456+
stared(hash: string, stared: boolean, refresh?: boolean): Promise<HashEntry>;
1457+
13951458
/**
13961459
* move many entries
13971460
*
@@ -2239,11 +2302,95 @@ class Remarkable implements RemarkableApi {
22392302
return await this.putPdf(visibleName, buffer, opts);
22402303
}
22412304

2305+
/** edit just a content entry */
2306+
async #editContentRaw(
2307+
id: string,
2308+
hash: string,
2309+
update: Partial<Content>,
2310+
): Promise<[RawListEntry, Promise<[void, void]>]> {
2311+
const entries = await this.raw.getEntries(hash);
2312+
const contInd = entries.findIndex((ent) => ent.id.endsWith(".content"));
2313+
const contEntry = entries[contInd];
2314+
if (contEntry === undefined) {
2315+
throw new Error("internal error: couldn't find content in entry hash");
2316+
}
2317+
const cont = await this.raw.getContent(contEntry.hash);
2318+
Object.assign(cont, update);
2319+
const [newContEntry, uploadCont] = await this.raw.putContent(
2320+
contEntry.id,
2321+
cont,
2322+
);
2323+
entries[contInd] = newContEntry;
2324+
const [result, uploadEntries] = await this.raw.putEntries(id, entries);
2325+
const upload = Promise.all([uploadCont, uploadEntries]);
2326+
return [result, upload];
2327+
}
2328+
2329+
/** fully sync a content edit */
2330+
async #editContent(
2331+
hash: string,
2332+
update: Partial<Content>,
2333+
expectedType: "DocumentType" | "CollectionType" | "TemplateType",
2334+
refresh: boolean,
2335+
): Promise<HashEntry> {
2336+
const [rootHash, generation] = await this.#getRootHash(refresh);
2337+
const entries = await this.raw.getEntries(rootHash);
2338+
const hashInd = entries.findIndex((ent) => ent.hash === hash);
2339+
const hashEnt = entries[hashInd];
2340+
if (hashEnt === undefined) {
2341+
throw new HashNotFoundError(hash);
2342+
}
2343+
2344+
const [[newEnt, uploadEnt], meta] = await Promise.all([
2345+
this.#editContentRaw(hashEnt.id, hash, update),
2346+
this.getMetadata(hash),
2347+
]);
2348+
if (meta.type !== expectedType) {
2349+
throw new Error(
2350+
`expected type ${expectedType} but got ${meta.type} for hash ${hash}`,
2351+
);
2352+
}
2353+
2354+
entries[hashInd] = newEnt;
2355+
const [rootEntry, uploadRoot] = await this.raw.putEntries("root", entries);
2356+
2357+
await Promise.all([uploadEnt, uploadRoot]);
2358+
await this.#putRootHash(rootEntry.hash, generation);
2359+
return { hash: newEnt.hash };
2360+
}
2361+
2362+
/** update document content */
2363+
async updateDocument(
2364+
hash: string,
2365+
content: Partial<DocumentContent>,
2366+
refresh: boolean = false,
2367+
): Promise<HashEntry> {
2368+
return await this.#editContent(hash, content, "DocumentType", refresh);
2369+
}
2370+
2371+
/** update collection content */
2372+
async updateCollection(
2373+
hash: string,
2374+
content: Partial<CollectionContent>,
2375+
refresh: boolean = false,
2376+
): Promise<HashEntry> {
2377+
return await this.#editContent(hash, content, "CollectionType", refresh);
2378+
}
2379+
2380+
/** update template content */
2381+
async updateTemplate(
2382+
hash: string,
2383+
content: Partial<TemplateContent>,
2384+
refresh: boolean = false,
2385+
): Promise<HashEntry> {
2386+
return await this.#editContent(hash, content, "TemplateType", refresh);
2387+
}
2388+
22422389
async #editMetaRaw(
22432390
id: string,
22442391
hash: string,
22452392
update: Partial<Metadata>,
2246-
): Promise<[RawListEntry, Promise<void>]> {
2393+
): Promise<[RawListEntry, Promise<[void, void]>]> {
22472394
const entries = await this.raw.getEntries(hash);
22482395
const metaInd = entries.findIndex((ent) => ent.id.endsWith(".metadata"));
22492396
const metaEntry = entries[metaInd];
@@ -2257,8 +2404,8 @@ class Remarkable implements RemarkableApi {
22572404
meta,
22582405
);
22592406
entries[metaInd] = newMetaEntry;
2260-
const [result, uploadentries] = await this.raw.putEntries(id, entries);
2261-
const upload = Promise.all([uploadMeta, uploadentries]).then(() => {});
2407+
const [result, uploadEntries] = await this.raw.putEntries(id, entries);
2408+
const upload = Promise.all([uploadMeta, uploadEntries]);
22622409
return [result, upload];
22632410
}
22642411

@@ -2318,6 +2465,15 @@ class Remarkable implements RemarkableApi {
23182465
return await this.#editMeta(hash, { visibleName }, refresh);
23192466
}
23202467

2468+
/** stared */
2469+
async stared(
2470+
hash: string,
2471+
stared: boolean,
2472+
refresh: boolean = false,
2473+
): Promise<HashEntry> {
2474+
return await this.#editMeta(hash, { pinned: stared }, refresh);
2475+
}
2476+
23212477
/** move many hashes */
23222478
async bulkMove(
23232479
hashes: readonly string[],
@@ -2346,7 +2502,7 @@ class Remarkable implements RemarkableApi {
23462502
const resolved = await Promise.all(
23472503
toUpdate.map(({ id, hash }) => this.#editMetaRaw(id, hash, { parent })),
23482504
);
2349-
const uploads: Promise<void>[] = [];
2505+
const uploads: Promise<[void, void]>[] = [];
23502506
const result: Record<string, string> = {};
23512507
for (const [i, [newEnt, upload]] of resolved.entries()) {
23522508
newEntries.push(newEnt);
@@ -2358,8 +2514,7 @@ class Remarkable implements RemarkableApi {
23582514
"root",
23592515
newEntries,
23602516
);
2361-
uploads.push(uploadRoot);
2362-
await Promise.all(uploads);
2517+
await Promise.all([Promise.all(uploads), uploadRoot]);
23632518

23642519
await this.#putRootHash(rootEntry.hash, generation);
23652520
return { hashes: result };

0 commit comments

Comments
 (0)