rmapi-js 11.2.0 → 12.0.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/README.md +3 -2
- package/dist/index.d.ts +72 -100
- package/dist/index.js +448 -89
- package/dist/raw.d.ts +47 -66
- package/dist/raw.js +216 -12
- package/dist/rmapi-js.esm.min.js +8 -8
- package/package.json +1 -1
package/dist/raw.d.ts
CHANGED
|
@@ -25,11 +25,19 @@ export type RequestMethod = "POST" | "GET" | "PUT" | "DELETE" | "PATCH" | "OPTIO
|
|
|
25
25
|
export type UploadMimeType = "application/pdf" | "application/epub+zip" | "folder";
|
|
26
26
|
/** the schema version */
|
|
27
27
|
export type SchemaVersion = 3 | 4;
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
/**
|
|
29
|
+
* a reference to stored cloud data: an id paired with the hash of its state
|
|
30
|
+
*
|
|
31
|
+
* This is the canonical way to point at something in the cloud. The `id` names
|
|
32
|
+
* *what*: a document's uuid at the high level, or a stored file name (like
|
|
33
|
+
* `<id>.content`) at the low level. The `hash` names *which version*, and
|
|
34
|
+
* changes every time that data mutates. Reads take one of these, and mutations
|
|
35
|
+
* return a fresh one with the new hash.
|
|
36
|
+
*/
|
|
37
|
+
export interface ItemRef {
|
|
38
|
+
/** the id of the referenced data: a document uuid, or a stored file name */
|
|
31
39
|
id: string;
|
|
32
|
-
/** the
|
|
40
|
+
/** the hash of the referenced state */
|
|
33
41
|
hash: string;
|
|
34
42
|
}
|
|
35
43
|
/**
|
|
@@ -40,13 +48,9 @@ export interface SimpleEntry {
|
|
|
40
48
|
* files, the high level entry will have the same hash and id as the low-level
|
|
41
49
|
* entry for that collection.
|
|
42
50
|
*/
|
|
43
|
-
export interface RawEntry {
|
|
51
|
+
export interface RawEntry extends ItemRef {
|
|
44
52
|
/** 80000000 for schema 3 collection type or 0 for schema 4 or schema 3 files or */
|
|
45
53
|
type: 80000000 | 0;
|
|
46
|
-
/** the hash of the collection this points to */
|
|
47
|
-
hash: string;
|
|
48
|
-
/** the unique id of the collection */
|
|
49
|
-
id: string;
|
|
50
54
|
/** the number of subfiles */
|
|
51
55
|
subfiles: number;
|
|
52
56
|
/** the total size of everything in the collection */
|
|
@@ -410,6 +414,10 @@ export interface Metadata {
|
|
|
410
414
|
}
|
|
411
415
|
/** parse and validate the json text of a `.metadata` file */
|
|
412
416
|
export declare function parseMetadata(text: string): Metadata;
|
|
417
|
+
type AuthedFetch = (method: RequestMethod, url: string, init?: {
|
|
418
|
+
body?: string | Uint8Array;
|
|
419
|
+
headers?: Record<string, string>;
|
|
420
|
+
}) => Promise<Response>;
|
|
413
421
|
/**
|
|
414
422
|
* access to the low-level reMarkable api
|
|
415
423
|
*
|
|
@@ -469,7 +477,10 @@ export declare function parseMetadata(text: string): Metadata;
|
|
|
469
477
|
*
|
|
470
478
|
* Generally all hashes are 64 character hex strings, and all ids are uuid4.
|
|
471
479
|
*/
|
|
472
|
-
export
|
|
480
|
+
export declare class RawRemarkable {
|
|
481
|
+
#private;
|
|
482
|
+
constructor(authedFetch: AuthedFetch, cache: Map<string, string | null>, rawHost: string, uploadHost: string);
|
|
483
|
+
/** make an authorized request to remarkable */
|
|
473
484
|
/**
|
|
474
485
|
* gets the root hash and the current generation
|
|
475
486
|
*
|
|
@@ -482,66 +493,62 @@ export interface RawRemarkableApi {
|
|
|
482
493
|
/**
|
|
483
494
|
* get the raw binary data associated with a hash
|
|
484
495
|
*
|
|
485
|
-
* @param
|
|
486
|
-
* `<id>.docSchema` / `"root.docSchema"`
|
|
487
|
-
* validates
|
|
488
|
-
*
|
|
496
|
+
* @param ref - a reference to the stored file. Its `id` is the logical file
|
|
497
|
+
* name (`<id>.<ext>` for files, or `<id>.docSchema` / `"root.docSchema"`
|
|
498
|
+
* for entry indexes), which reMarkable validates against the rm-filename
|
|
499
|
+
* header. Sub-entries from {@link getEntries | `getEntries`} can be passed
|
|
500
|
+
* directly.
|
|
489
501
|
* @returns the data
|
|
490
502
|
*/
|
|
491
|
-
getHash(
|
|
503
|
+
getHash({ id: fileName, hash }: ItemRef): Promise<Uint8Array>;
|
|
492
504
|
/**
|
|
493
505
|
* get raw text data associated with a hash
|
|
494
506
|
*
|
|
495
507
|
* We assume text data are small, and so cache the entire text. If you want to
|
|
496
508
|
* avoid this, use {@link getHash | `getHash`} combined with a TextDecoder.
|
|
497
|
-
|
|
498
|
-
* @param
|
|
499
|
-
* @param hash - the hash to get text for
|
|
509
|
+
*
|
|
510
|
+
* @param ref - a reference to the stored file (see {@link getHash})
|
|
500
511
|
* @returns the text
|
|
501
512
|
*/
|
|
502
|
-
getText(
|
|
513
|
+
getText({ id: fileName, hash }: ItemRef): Promise<string>;
|
|
503
514
|
/**
|
|
504
515
|
* get the entries associated with a list hash
|
|
505
516
|
*
|
|
506
517
|
* A list hash is the root hash, or any hash with the type 80000000. NOTE
|
|
507
518
|
* these are hashed differently than files.
|
|
508
|
-
|
|
509
|
-
* @param
|
|
510
|
-
* for a sub-document's entry index
|
|
511
|
-
* @param hash - the hash to get entries for
|
|
519
|
+
*
|
|
520
|
+
* @param ref - a reference whose `id` is `"root.docSchema"` for the root, or
|
|
521
|
+
* `"<id>.docSchema"` for a sub-document's entry index
|
|
512
522
|
* @returns the entries
|
|
513
523
|
*/
|
|
514
|
-
getEntries(
|
|
524
|
+
getEntries(ref: ItemRef): Promise<Entries>;
|
|
515
525
|
/**
|
|
516
526
|
* get the parsed and validated `Content` of a content hash
|
|
517
527
|
*
|
|
518
528
|
* Use {@link getText | `getText`} combined with `JSON.parse` to bypass
|
|
519
529
|
* validation
|
|
520
|
-
|
|
521
|
-
* @param
|
|
522
|
-
* @param hash - the hash to get Content for
|
|
530
|
+
*
|
|
531
|
+
* @param ref - a reference to the stored file, typically `"<id>.content"`
|
|
523
532
|
* @returns the content
|
|
524
533
|
*/
|
|
525
|
-
getContent(
|
|
534
|
+
getContent(ref: ItemRef): Promise<Content>;
|
|
526
535
|
/**
|
|
527
536
|
* get the parsed and validated `Metadata` of a metadata hash
|
|
528
537
|
*
|
|
529
538
|
* Use {@link getText | `getText`} combined with `JSON.parse` to bypass
|
|
530
539
|
* validation
|
|
531
|
-
|
|
532
|
-
* @param
|
|
533
|
-
* @param hash - the hash to get Metadata for
|
|
540
|
+
*
|
|
541
|
+
* @param ref - a reference to the stored file, typically `"<id>.metadata"`
|
|
534
542
|
* @returns the metadata
|
|
535
543
|
*/
|
|
536
|
-
getMetadata(
|
|
544
|
+
getMetadata(ref: ItemRef): Promise<Metadata>;
|
|
537
545
|
/**
|
|
538
546
|
* get the parsed reMarkable lines (`.rm`) drawing of a page hash
|
|
539
|
-
|
|
540
|
-
* @param
|
|
541
|
-
* @param hash - the hash to get the page for
|
|
547
|
+
*
|
|
548
|
+
* @param ref - a reference to the stored file, typically `"<id>/<pageid>.rm"`
|
|
542
549
|
* @returns the parsed page
|
|
543
550
|
*/
|
|
544
|
-
getRm(
|
|
551
|
+
getRm(ref: ItemRef): Promise<RmPage>;
|
|
545
552
|
/**
|
|
546
553
|
* the same as {@link putFile | `putFile`} but rendering an `RmPage` to `.rm`
|
|
547
554
|
* bytes
|
|
@@ -585,7 +592,7 @@ export interface RawRemarkableApi {
|
|
|
585
592
|
*/
|
|
586
593
|
putFile(fileName: string, bytes: Uint8Array): Promise<[RawEntry, Promise<void>]>;
|
|
587
594
|
/** the same as {@link putFile | `putFile`} but with caching for text */
|
|
588
|
-
putText(fileName: string,
|
|
595
|
+
putText(fileName: string, text: string): Promise<[RawEntry, Promise<void>]>;
|
|
589
596
|
/** the same as {@link putText | `putText`} but with extra validation for Content */
|
|
590
597
|
putContent(fileName: string, content: Content): Promise<[RawEntry, Promise<void>]>;
|
|
591
598
|
/** the same as {@link putText | `putText`} but with extra validation for Metadata */
|
|
@@ -624,10 +631,10 @@ export interface RawRemarkableApi {
|
|
|
624
631
|
* @param visibleName - the name of the file as it should appear on the reMarkable
|
|
625
632
|
* @param bytes - the bytes of the file to upload
|
|
626
633
|
* @param mime - the mime type of the file to upload
|
|
627
|
-
|
|
634
|
+
|
|
628
635
|
* @returns a simple entry with the id and hash of the uploaded file
|
|
629
636
|
*/
|
|
630
|
-
uploadFile(visibleName: string, bytes: Uint8Array, mime: UploadMimeType): Promise<
|
|
637
|
+
uploadFile(visibleName: string, bytes: Uint8Array, mime: UploadMimeType): Promise<ItemRef>;
|
|
631
638
|
/**
|
|
632
639
|
* dump the current cache to a string to preserve between session
|
|
633
640
|
*
|
|
@@ -637,30 +644,4 @@ export interface RawRemarkableApi {
|
|
|
637
644
|
/** completely clear the cache */
|
|
638
645
|
clearCache(): void;
|
|
639
646
|
}
|
|
640
|
-
type
|
|
641
|
-
body?: string | Uint8Array;
|
|
642
|
-
headers?: Record<string, string>;
|
|
643
|
-
}) => Promise<Response>;
|
|
644
|
-
export declare class RawRemarkable implements RawRemarkableApi {
|
|
645
|
-
#private;
|
|
646
|
-
constructor(authedFetch: AuthedFetch, cache: Map<string, string | null>, rawHost: string, uploadHost: string);
|
|
647
|
-
/** make an authorized request to remarkable */
|
|
648
|
-
getRootHash(): Promise<[string, number, SchemaVersion]>;
|
|
649
|
-
getHash(fileName: string, hash: string): Promise<Uint8Array>;
|
|
650
|
-
getText(fileName: string, hash: string): Promise<string>;
|
|
651
|
-
getEntries(fileName: string, hash: string): Promise<Entries>;
|
|
652
|
-
getContent(fileName: string, hash: string): Promise<Content>;
|
|
653
|
-
getMetadata(fileName: string, hash: string): Promise<Metadata>;
|
|
654
|
-
getRm(fileName: string, hash: string): Promise<RmPage>;
|
|
655
|
-
putRm(fileName: string, page: RmPageV5): Promise<[RawEntry, Promise<void>]>;
|
|
656
|
-
putRootHash(hash: string, generation: number, broadcast?: boolean): Promise<[string, number]>;
|
|
657
|
-
putFile(fileName: string, bytes: Uint8Array): Promise<[RawEntry, Promise<void>]>;
|
|
658
|
-
putText(fileName: string, text: string): Promise<[RawEntry, Promise<void>]>;
|
|
659
|
-
putContent(fileName: string, content: Content): Promise<[RawEntry, Promise<void>]>;
|
|
660
|
-
putMetadata(fileName: string, metadata: Metadata): Promise<[RawEntry, Promise<void>]>;
|
|
661
|
-
putEntries(id: string, entries: readonly RawEntry[], schemaVersion: SchemaVersion): Promise<[RawEntry, Promise<void>]>;
|
|
662
|
-
uploadFile(visibleName: string, bytes: Uint8Array, mime: UploadMimeType): Promise<SimpleEntry>;
|
|
663
|
-
dumpCache(): string;
|
|
664
|
-
clearCache(): void;
|
|
665
|
-
}
|
|
666
|
-
export {};
|
|
647
|
+
export type { RawRemarkable as RawRemarkableApi };
|
package/dist/raw.js
CHANGED
|
@@ -233,7 +233,7 @@ const rootHash = z
|
|
|
233
233
|
schemaVersion: z.number().int().nonnegative(),
|
|
234
234
|
})
|
|
235
235
|
.passthrough();
|
|
236
|
-
const
|
|
236
|
+
const nativeItemRef = z
|
|
237
237
|
.object({
|
|
238
238
|
docID: z.string(),
|
|
239
239
|
hash: z.string(),
|
|
@@ -267,6 +267,65 @@ function parseRawEntryLine(line) {
|
|
|
267
267
|
throw new Error(`line '${line}' was not formatted correctly`);
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
|
+
/**
|
|
271
|
+
* access to the low-level reMarkable api
|
|
272
|
+
*
|
|
273
|
+
* This class gives more granualar access to the reMarkable cloud, but is more
|
|
274
|
+
* dangerous.
|
|
275
|
+
*
|
|
276
|
+
* ## Overview
|
|
277
|
+
*
|
|
278
|
+
* reMarkable uses an immutable file system, where each file is referenced by
|
|
279
|
+
* the 32 byte sha256 hash of its contents. Each file also has an id used to
|
|
280
|
+
* keep track of updates, so to "update" a file, you upload a new file, and
|
|
281
|
+
* change the hash associated with it's id.
|
|
282
|
+
*
|
|
283
|
+
* Each "item" (a document or a collection) is actually a list of files.
|
|
284
|
+
* The whole reMarkable state is then a list of these lists. Finally, the hash
|
|
285
|
+
* of that list is called the rootHash. To update anything, you have to update
|
|
286
|
+
* the root hash to point to a new list of updated items.
|
|
287
|
+
*
|
|
288
|
+
* This can be dangerous, as corrupting the root hash can destroy all of your
|
|
289
|
+
* files. It is therefore highly recommended to save your current root hash
|
|
290
|
+
* ({@link getRootHash | `getRootHash`}) before using this api to attempt file
|
|
291
|
+
* writes, so you can recover a previous "snapshot" should anything go wrong.
|
|
292
|
+
*
|
|
293
|
+
* ## Items
|
|
294
|
+
*
|
|
295
|
+
* Each item is a collection of individual files. Using
|
|
296
|
+
* {@link getEntries | `getEntries`} on the root hash will give you a list
|
|
297
|
+
* entries that correspond to items. Using `getEntries` on any of those items
|
|
298
|
+
* will get you the files that make up that item.
|
|
299
|
+
*
|
|
300
|
+
* The documented files are:
|
|
301
|
+
* - `<docid>.pdf` - a raw pdf document
|
|
302
|
+
* - `<docid>.epub` - a raw epub document
|
|
303
|
+
* - `<docid>.content` - a json file roughly describing document properties (see {@link DocumentContent | `DocumentContent`})
|
|
304
|
+
* - `<docid>.metadata` - metadata about the document (see {@link Metadata | `Metadata`})
|
|
305
|
+
* - `<docid>.pagedata` - a text file where each line is the template of that page
|
|
306
|
+
* - `<docid>/<pageid>.rm` - [speculative] raw remarkable vectors, text, etc
|
|
307
|
+
* - `<docid>/<pageid>-metadata.json` - [speculative] metadata about the individual page
|
|
308
|
+
* - `<docid>.highlights/<pageid>.json` - [speculative] highlights on the page
|
|
309
|
+
*
|
|
310
|
+
* Some items will have both a `.pdf` and `.epub` file, likely due to preparing
|
|
311
|
+
* for export. Collections only have `.content` and `.metadata` files, with
|
|
312
|
+
* `.content` only containing tags.
|
|
313
|
+
*
|
|
314
|
+
* ## Caching
|
|
315
|
+
*
|
|
316
|
+
* Since everything is tied to the hash of it's contents, we can agressively
|
|
317
|
+
* cache results. We assume that text contents are "small" and so fully cache
|
|
318
|
+
* them, where as binary files we treat as large and only store that we know
|
|
319
|
+
* they exist to prevent future writes.
|
|
320
|
+
*
|
|
321
|
+
* By default, this only persists as long as the api instance is alive. However,
|
|
322
|
+
* for performance reasons, you should call {@link dumpCache | `dumpCache`} to
|
|
323
|
+
* persist the cache between sessions.
|
|
324
|
+
*
|
|
325
|
+
* @remarks
|
|
326
|
+
*
|
|
327
|
+
* Generally all hashes are 64 character hex strings, and all ids are uuid4.
|
|
328
|
+
*/
|
|
270
329
|
export class RawRemarkable {
|
|
271
330
|
#authedFetch;
|
|
272
331
|
#rawHost;
|
|
@@ -288,6 +347,14 @@ export class RawRemarkable {
|
|
|
288
347
|
this.#uploadHost = uploadHost;
|
|
289
348
|
}
|
|
290
349
|
/** make an authorized request to remarkable */
|
|
350
|
+
/**
|
|
351
|
+
* gets the root hash and the current generation
|
|
352
|
+
*
|
|
353
|
+
* When calling `putRootHash`, you should pass the generation you got from
|
|
354
|
+
* this call. That way you tell reMarkable you're updating the previous state.
|
|
355
|
+
*
|
|
356
|
+
* @returns the root hash and the current generation
|
|
357
|
+
*/
|
|
291
358
|
async getRootHash() {
|
|
292
359
|
const res = await this.#authedFetch("GET", `${this.#rawHost}/sync/v4/root`);
|
|
293
360
|
const raw = await res.text();
|
|
@@ -312,7 +379,17 @@ export class RawRemarkable {
|
|
|
312
379
|
const raw = await resp.arrayBuffer();
|
|
313
380
|
return new Uint8Array(raw);
|
|
314
381
|
}
|
|
315
|
-
|
|
382
|
+
/**
|
|
383
|
+
* get the raw binary data associated with a hash
|
|
384
|
+
*
|
|
385
|
+
* @param ref - a reference to the stored file. Its `id` is the logical file
|
|
386
|
+
* name (`<id>.<ext>` for files, or `<id>.docSchema` / `"root.docSchema"`
|
|
387
|
+
* for entry indexes), which reMarkable validates against the rm-filename
|
|
388
|
+
* header. Sub-entries from {@link getEntries | `getEntries`} can be passed
|
|
389
|
+
* directly.
|
|
390
|
+
* @returns the data
|
|
391
|
+
*/
|
|
392
|
+
async getHash({ id: fileName, hash }) {
|
|
316
393
|
const cached = this.#cache.get(hash);
|
|
317
394
|
if (cached != null) {
|
|
318
395
|
const enc = new TextEncoder();
|
|
@@ -328,7 +405,16 @@ export class RawRemarkable {
|
|
|
328
405
|
return res;
|
|
329
406
|
}
|
|
330
407
|
}
|
|
331
|
-
|
|
408
|
+
/**
|
|
409
|
+
* get raw text data associated with a hash
|
|
410
|
+
*
|
|
411
|
+
* We assume text data are small, and so cache the entire text. If you want to
|
|
412
|
+
* avoid this, use {@link getHash | `getHash`} combined with a TextDecoder.
|
|
413
|
+
*
|
|
414
|
+
* @param ref - a reference to the stored file (see {@link getHash})
|
|
415
|
+
* @returns the text
|
|
416
|
+
*/
|
|
417
|
+
async getText({ id: fileName, hash }) {
|
|
332
418
|
const cached = this.#cache.get(hash);
|
|
333
419
|
if (cached != null) {
|
|
334
420
|
return cached;
|
|
@@ -342,8 +428,18 @@ export class RawRemarkable {
|
|
|
342
428
|
return res;
|
|
343
429
|
}
|
|
344
430
|
}
|
|
345
|
-
|
|
346
|
-
|
|
431
|
+
/**
|
|
432
|
+
* get the entries associated with a list hash
|
|
433
|
+
*
|
|
434
|
+
* A list hash is the root hash, or any hash with the type 80000000. NOTE
|
|
435
|
+
* these are hashed differently than files.
|
|
436
|
+
*
|
|
437
|
+
* @param ref - a reference whose `id` is `"root.docSchema"` for the root, or
|
|
438
|
+
* `"<id>.docSchema"` for a sub-document's entry index
|
|
439
|
+
* @returns the entries
|
|
440
|
+
*/
|
|
441
|
+
async getEntries(ref) {
|
|
442
|
+
const rawFile = await this.getText(ref);
|
|
347
443
|
const [version, ...rest] = rawFile.slice(0, -1).split("\n");
|
|
348
444
|
if (version === "3") {
|
|
349
445
|
return { entries: rest.map(parseRawEntryLine) };
|
|
@@ -371,20 +467,50 @@ export class RawRemarkable {
|
|
|
371
467
|
throw new Error(`schema version ${version} not supported`);
|
|
372
468
|
}
|
|
373
469
|
}
|
|
374
|
-
|
|
375
|
-
|
|
470
|
+
/**
|
|
471
|
+
* get the parsed and validated `Content` of a content hash
|
|
472
|
+
*
|
|
473
|
+
* Use {@link getText | `getText`} combined with `JSON.parse` to bypass
|
|
474
|
+
* validation
|
|
475
|
+
*
|
|
476
|
+
* @param ref - a reference to the stored file, typically `"<id>.content"`
|
|
477
|
+
* @returns the content
|
|
478
|
+
*/
|
|
479
|
+
async getContent(ref) {
|
|
480
|
+
const raw = await this.getText(ref);
|
|
376
481
|
const loaded = JSON.parse(raw);
|
|
377
482
|
return content.parse(loaded);
|
|
378
483
|
}
|
|
379
|
-
|
|
380
|
-
|
|
484
|
+
/**
|
|
485
|
+
* get the parsed and validated `Metadata` of a metadata hash
|
|
486
|
+
*
|
|
487
|
+
* Use {@link getText | `getText`} combined with `JSON.parse` to bypass
|
|
488
|
+
* validation
|
|
489
|
+
*
|
|
490
|
+
* @param ref - a reference to the stored file, typically `"<id>.metadata"`
|
|
491
|
+
* @returns the metadata
|
|
492
|
+
*/
|
|
493
|
+
async getMetadata(ref) {
|
|
494
|
+
const raw = await this.getText(ref);
|
|
381
495
|
const loaded = JSON.parse(raw);
|
|
382
496
|
return metadata.parse(loaded);
|
|
383
497
|
}
|
|
384
|
-
|
|
385
|
-
|
|
498
|
+
/**
|
|
499
|
+
* get the parsed reMarkable lines (`.rm`) drawing of a page hash
|
|
500
|
+
*
|
|
501
|
+
* @param ref - a reference to the stored file, typically `"<id>/<pageid>.rm"`
|
|
502
|
+
* @returns the parsed page
|
|
503
|
+
*/
|
|
504
|
+
async getRm(ref) {
|
|
505
|
+
const bytes = await this.getHash(ref);
|
|
386
506
|
return parseRm(bytes);
|
|
387
507
|
}
|
|
508
|
+
/**
|
|
509
|
+
* the same as {@link putFile | `putFile`} but rendering an `RmPage` to `.rm`
|
|
510
|
+
* bytes
|
|
511
|
+
*
|
|
512
|
+
* Only version 3 and 5 pages can be rendered; version 6 pages are read-only.
|
|
513
|
+
*/
|
|
388
514
|
async putRm(fileName, page) {
|
|
389
515
|
if (!fileName.endsWith(".rm")) {
|
|
390
516
|
throw new Error(`fileName ${fileName} did not end with '.rm'`);
|
|
@@ -393,6 +519,25 @@ export class RawRemarkable {
|
|
|
393
519
|
return await this.putFile(fileName, serializeRm(page));
|
|
394
520
|
}
|
|
395
521
|
}
|
|
522
|
+
/**
|
|
523
|
+
* update the current root hash
|
|
524
|
+
*
|
|
525
|
+
* This will fail if generation doesn't match the current server generation.
|
|
526
|
+
* This ensures that you are updating what you expect. IF you get a
|
|
527
|
+
* {@link GenerationError | `GenerationError`}, that indicates that the server
|
|
528
|
+
* was updated after you last got the generation. You should call
|
|
529
|
+
* {@link getRootHash | `getRootHash`} and then recompute the changes you want
|
|
530
|
+
* from the new root hash. If you ignore the update hash value and just call
|
|
531
|
+
* `putRootHash` again, you will overwrite the changes made by the other
|
|
532
|
+
* update.
|
|
533
|
+
*
|
|
534
|
+
* @param hash - the new root hash
|
|
535
|
+
* @param generation - the generation of the current root hash
|
|
536
|
+
* @param broadcast - [unknown] an option in the request
|
|
537
|
+
*
|
|
538
|
+
* @throws GenerationError if the generation doesn't match the current server generation
|
|
539
|
+
* @returns the new root hash and the new generation
|
|
540
|
+
*/
|
|
396
541
|
async putRootHash(hash, generation, broadcast = true) {
|
|
397
542
|
if (!Number.isSafeInteger(generation)) {
|
|
398
543
|
throw new Error(`generation ${generation} was not a safe integer`);
|
|
@@ -437,6 +582,20 @@ export class RawRemarkable {
|
|
|
437
582
|
}
|
|
438
583
|
}
|
|
439
584
|
}
|
|
585
|
+
/**
|
|
586
|
+
* put a raw onto the server
|
|
587
|
+
*
|
|
588
|
+
* This returns the new expeced entry of the file you uploaded, and a promise
|
|
589
|
+
* to finish the upload successful. By splitting these two operations you can
|
|
590
|
+
* start using the uploaded entry while file finishes uploading.
|
|
591
|
+
*
|
|
592
|
+
* NOTE: This won't update the state of the reMarkable until this entry is
|
|
593
|
+
* incorporated into the root hash.
|
|
594
|
+
*
|
|
595
|
+
* @param fileName - the file name to upload (e.g. `<id>.pdf`)
|
|
596
|
+
* @param bytes - the bytes to upload
|
|
597
|
+
* @returns the new entry and a promise to finish the upload
|
|
598
|
+
*/
|
|
440
599
|
async putFile(fileName, bytes) {
|
|
441
600
|
const hash = await digest(bytes);
|
|
442
601
|
const res = {
|
|
@@ -448,6 +607,7 @@ export class RawRemarkable {
|
|
|
448
607
|
};
|
|
449
608
|
return [res, this.#putFile(fileName, hash, bytes)];
|
|
450
609
|
}
|
|
610
|
+
/** the same as {@link putFile | `putFile`} but with caching for text */
|
|
451
611
|
async putText(fileName, text) {
|
|
452
612
|
const enc = new TextEncoder();
|
|
453
613
|
const bytes = enc.encode(text);
|
|
@@ -460,6 +620,7 @@ export class RawRemarkable {
|
|
|
460
620
|
}),
|
|
461
621
|
];
|
|
462
622
|
}
|
|
623
|
+
/** the same as {@link putText | `putText`} but with extra validation for Content */
|
|
463
624
|
async putContent(fileName, content) {
|
|
464
625
|
if (!fileName.endsWith(".content")) {
|
|
465
626
|
throw new Error(`fileName ${fileName} did not end with '.content'`);
|
|
@@ -468,6 +629,7 @@ export class RawRemarkable {
|
|
|
468
629
|
return await this.putText(fileName, JSON.stringify(content));
|
|
469
630
|
}
|
|
470
631
|
}
|
|
632
|
+
/** the same as {@link putText | `putText`} but with extra validation for Metadata */
|
|
471
633
|
async putMetadata(fileName, metadata) {
|
|
472
634
|
if (!fileName.endsWith(".metadata")) {
|
|
473
635
|
throw new Error(`fileName ${fileName} did not end with '.metadata'`);
|
|
@@ -476,6 +638,29 @@ export class RawRemarkable {
|
|
|
476
638
|
return await this.putText(fileName, JSON.stringify(metadata));
|
|
477
639
|
}
|
|
478
640
|
}
|
|
641
|
+
/**
|
|
642
|
+
* put a set of entries to make an entry list file
|
|
643
|
+
*
|
|
644
|
+
* To fully upload an item:
|
|
645
|
+
* 1. upload all the constituent files and metadata
|
|
646
|
+
* 2. call this with all of the entries
|
|
647
|
+
* 3. append this entry to the root entry and call this again to update this root list
|
|
648
|
+
* 4. put the new root hash
|
|
649
|
+
*
|
|
650
|
+
* NOTE: reMarkable currently rejects newly written schema 3 root indexes
|
|
651
|
+
* with a 400 "Software must be updated" error, even for accounts that still
|
|
652
|
+
* report schema 3, so the root list should always be written as schema 4. A
|
|
653
|
+
* warning is logged if a schema 3 root index is written.
|
|
654
|
+
*
|
|
655
|
+
* @param id - the id of the list to upload - this should be the item id if
|
|
656
|
+
* uploading an item list, or "root" if uploading a new root list. Note the
|
|
657
|
+
* asymmetry with {@link getEntries | `getEntries`}: `getEntries` takes the
|
|
658
|
+
* full `"<id>.docSchema"` file name, whereas `putEntries` takes the bare id
|
|
659
|
+
* and appends `.docSchema` (and special-cases `"root"`) itself.
|
|
660
|
+
* @param entries - the entries to upload
|
|
661
|
+
*
|
|
662
|
+
* @returns the new list entry and a promise to finish the upload
|
|
663
|
+
*/
|
|
479
664
|
async putEntries(id, entries, schemaVersion) {
|
|
480
665
|
if (id === "root" && schemaVersion === 3) {
|
|
481
666
|
console.warn('writing a schema 3 root index, which reMarkable rejects with a 400 "Software must be updated" error; write the root index with schema version 4 instead');
|
|
@@ -520,6 +705,19 @@ export class RawRemarkable {
|
|
|
520
705
|
};
|
|
521
706
|
return [res, this.#putFile(`${id}.docSchema`, hash, entryBuff)];
|
|
522
707
|
}
|
|
708
|
+
/**
|
|
709
|
+
* upload a file to the reMarkable cloud using the simple api
|
|
710
|
+
*
|
|
711
|
+
* This api is the same as used by the native reMarkable extension and works
|
|
712
|
+
* even if the backend schema version is version 4. Setting mime to "folder"
|
|
713
|
+
* allows folder creation.
|
|
714
|
+
*
|
|
715
|
+
* @param visibleName - the name of the file as it should appear on the reMarkable
|
|
716
|
+
* @param bytes - the bytes of the file to upload
|
|
717
|
+
* @param mime - the mime type of the file to upload
|
|
718
|
+
|
|
719
|
+
* @returns a simple entry with the id and hash of the uploaded file
|
|
720
|
+
*/
|
|
523
721
|
async uploadFile(visibleName, bytes, mime) {
|
|
524
722
|
const enc = new TextEncoder();
|
|
525
723
|
const meta = enc
|
|
@@ -534,12 +732,18 @@ export class RawRemarkable {
|
|
|
534
732
|
},
|
|
535
733
|
});
|
|
536
734
|
const loaded = (await resp.json());
|
|
537
|
-
const { docID, hash } =
|
|
735
|
+
const { docID, hash } = nativeItemRef.parse(loaded);
|
|
538
736
|
return { id: docID, hash };
|
|
539
737
|
}
|
|
738
|
+
/**
|
|
739
|
+
* dump the current cache to a string to preserve between session
|
|
740
|
+
*
|
|
741
|
+
* @returns a serialized version of the cache to pass to a new api instance
|
|
742
|
+
*/
|
|
540
743
|
dumpCache() {
|
|
541
744
|
return JSON.stringify(Object.fromEntries(this.#cache));
|
|
542
745
|
}
|
|
746
|
+
/** completely clear the cache */
|
|
543
747
|
clearCache() {
|
|
544
748
|
this.#cache.clear();
|
|
545
749
|
}
|