rmapi-js 6.0.0 → 8.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/dist/index.d.ts CHANGED
@@ -7,6 +7,62 @@ export interface Tag {
7
7
  /** the timestamp when this tag was added */
8
8
  timestamp: number;
9
9
  }
10
+ /** a tag for individual pages */
11
+ export interface PageTag extends Tag {
12
+ /** the id of the page this is on */
13
+ pageId: string;
14
+ }
15
+ /** the type of files reMarkable supports */
16
+ export type FileType = "epub" | "pdf" | "notebook";
17
+ /** all supported document orientations */
18
+ export type Orientation = "portrait" | "landscape";
19
+ /** all supported text alignments */
20
+ export type TextAlignment = "justify" | "left";
21
+ /** types of zoom modes for documents, applies primarily to pdf files */
22
+ export type ZoomMode = "bestFit" | "customFit" | "fitToHeight" | "fitToWidth";
23
+ /**
24
+ * types of background filter
25
+ *
26
+ * off has no background filter, best for images, full page applies the high
27
+ * contrast filter to the entire page. If this is omitted, reMarkable will try
28
+ * to apply the filter only to text areas.
29
+ */
30
+ export type BackgroundFilter = "off" | "fullpage";
31
+ /**
32
+ * the low-level entry corresponding to a collection of files
33
+ *
34
+ * A collection could be for the root collection, or for an individual document,
35
+ * which is often a collection of files. If an entry represents a collection of
36
+ * files, the high level entry will have the same hash and id as the low-level
37
+ * entry for that collection.
38
+ */
39
+ export interface RawListEntry {
40
+ /** collection type (80000000) */
41
+ type: 80000000;
42
+ /** the hash of the collection this points to */
43
+ hash: string;
44
+ /** the unique id of the collection */
45
+ id: string;
46
+ /** the number of subfiles */
47
+ subfiles: number;
48
+ /** the total size of everything in the collection */
49
+ size: number;
50
+ }
51
+ /** the low-level entry for a single file */
52
+ export interface RawFileEntry {
53
+ /** file type (0) */
54
+ type: 0;
55
+ /** the hash of the file this points to */
56
+ hash: string;
57
+ /** the unique id of the file */
58
+ id: string;
59
+ /** the number of subfiles, always zero */
60
+ subfiles: 0;
61
+ /** the size of the file in bytes */
62
+ size: number;
63
+ }
64
+ /** a low-level stored entry */
65
+ export type RawEntry = RawListEntry | RawFileEntry;
10
66
  /** common properties shared by collections and documents */
11
67
  export interface EntryCommon {
12
68
  /** the document id, a uuid4 */
@@ -45,10 +101,10 @@ export interface DocumentType extends EntryCommon {
45
101
  }
46
102
  /** a remarkable entry for cloud items */
47
103
  export type Entry = CollectionEntry | DocumentType;
48
- /** an simple entry produced by the upload api */
49
- export interface UploadEntry {
104
+ /** an simple entry without any extra information */
105
+ export interface SimpleEntry {
50
106
  /** the document id */
51
- docID: string;
107
+ id: string;
52
108
  /** the document hash */
53
109
  hash: string;
54
110
  }
@@ -62,35 +118,17 @@ export interface HashesEntry {
62
118
  /** the mapping from old to new hashes */
63
119
  hashes: Record<string, string>;
64
120
  }
65
- /** stripped down version of RequestInit */
66
- export interface RequestInitLike {
67
- /** request method */
68
- readonly method?: RequestMethod | undefined;
69
- /** request headers */
70
- readonly headers?: Record<string, string>;
71
- /** request body */
72
- readonly body?: ArrayBuffer | string;
73
- }
74
- /** stripped down version of Headers */
75
- export interface HeadersLike {
76
- /** get a specific header value */
77
- get(key: string): string | null;
78
- }
79
- /** stripped down version of Response */
80
- export interface ResponseLike {
81
- /** true if request was successful */
82
- ok: boolean;
83
- /** http status */
84
- status: number;
85
- /** text associated with status */
86
- statusText: string;
87
- /** get response body as text */
88
- text(): Promise<string>;
89
- }
90
- /** stripped down version of fetch */
91
- export interface FetchLike {
92
- /** the rough interface to fetch */
93
- (url: string, options?: RequestInitLike): Promise<ResponseLike>;
121
+ /** An error that gets thrown when the backend while trying to update
122
+ *
123
+ * IF you encounter this error, you likely just need to try th request again. If
124
+ * you're trying to do several high-level `put` operations simultaneously,
125
+ * you'll likely encounter this error. You should either try to do them
126
+ * serially, or call the low level api directly to do one generation update.
127
+ *
128
+ * @see {@link RawRemarkableApi | `RawRemarkableApi`}
129
+ */
130
+ export declare class GenerationError extends Error {
131
+ constructor();
94
132
  }
95
133
  /** an error that results from a failed request */
96
134
  export declare class ResponseError extends Error {
@@ -108,6 +146,12 @@ export declare class ValidationError extends Error {
108
146
  readonly regex: RegExp;
109
147
  constructor(field: string, regex: RegExp, message: string);
110
148
  }
149
+ /** an error that results while supplying a hash not found in the entries of the root hash */
150
+ export declare class HashNotFoundError extends Error {
151
+ /** the hash that couldn't be found */
152
+ readonly hash: string;
153
+ constructor(hash: string);
154
+ }
111
155
  /** options for registering with the api */
112
156
  export interface RegisterOptions {
113
157
  /**
@@ -123,8 +167,6 @@ export interface RegisterOptions {
123
167
  uuid?: string;
124
168
  /** The host to use for authorization requests */
125
169
  authHost?: string;
126
- /** a function for making fetch requests, see {@link RemarkableOptions.fetch} for more info */
127
- fetch?: FetchLike;
128
170
  }
129
171
  /**
130
172
  * register a device and get the token needed to access the api
@@ -136,36 +178,618 @@ export interface RegisterOptions {
136
178
  * @param code - the eight letter code a user got from `https://my.remarkable.com/device/browser/connect`.
137
179
  * @returns the device token necessary for creating an api instace. These never expire so persist as long as necessary.
138
180
  */
139
- export declare function register(code: string, { deviceDesc, uuid, authHost, fetch, }?: RegisterOptions): Promise<string>;
140
- /** options for getting responses */
141
- export interface GetOptions {
181
+ export declare function register(code: string, { deviceDesc, uuid, authHost, }?: RegisterOptions): Promise<string>;
182
+ /** options available when uploading a document */
183
+ export interface UploadOptions {
184
+ /** an optional parent id to set when uploading */
185
+ parent?: string;
186
+ }
187
+ /** document metadata stored in {@link Content} */
188
+ export interface DocumentMetadata {
189
+ /** a list of authors as a string */
190
+ authors?: string[];
191
+ /** the title as a string */
192
+ title?: string;
193
+ /** the publication date as an ISO date or timestamp */
194
+ publicationDate?: string;
195
+ /** the publisher */
196
+ publisher?: string;
197
+ }
198
+ /** [speculative] metadata stored about keyboard interactions */
199
+ export interface KeyboardMetadata {
200
+ /** [unknown] */
201
+ count: number;
202
+ /** [unknown] */
203
+ timestamp: number;
204
+ }
205
+ /** a c-page value who's type is a string */
206
+ export interface CPageStringValue {
207
+ /** a pseudo-timestamp of the form "1:1" or "1:2" */
208
+ timestamp: string;
209
+ /** the stored value */
210
+ value: string;
211
+ }
212
+ /** a c-page value who's type is a string */
213
+ export interface CPageNumberValue {
214
+ /** a pseudo-timestamp of the form "1:1" or "1:2" */
215
+ timestamp: string;
216
+ /** the stored value */
217
+ value: number;
218
+ }
219
+ /** [speculative] information about an individual page */
220
+ export interface CPagePage {
221
+ /** [speculative] the page id */
222
+ id: string;
223
+ /** [unknown] values are like "aa", "ab", "ba", etc. */
224
+ idx: CPageStringValue;
225
+ /** [unknown] */
226
+ redir?: CPageNumberValue;
227
+ /** [speculative] the template name of the page */
228
+ template?: CPageStringValue;
229
+ /** [unknown] the value is a timestamp */
230
+ scrollTime?: CPageStringValue;
231
+ /** [unknown] */
232
+ verticalScroll?: CPageNumberValue;
233
+ /** [unknown] */
234
+ deleted?: CPageNumberValue;
235
+ }
236
+ /** [unknown] */
237
+ export interface CPageUUID {
238
+ /** [unknown] */
239
+ first: string;
240
+ /** [unknown] */
241
+ second: number;
242
+ }
243
+ /** [unknown] metadata about pages */
244
+ export interface CPages {
245
+ /** [speculative] the last time the document was opened */
246
+ lastOpened: CPageStringValue;
247
+ /** [unknown] */
248
+ original: CPageNumberValue;
249
+ /** [speculative] information about individual pages */
250
+ pages: CPagePage[];
251
+ /** [unknown] */
252
+ uuids: CPageUUID[];
253
+ }
254
+ /** the content metadata for collections (folders) */
255
+ export interface CollectionContent {
256
+ /** the tags for the collection */
257
+ tags?: Tag[];
258
+ /** collections don't have a file type */
259
+ fileType?: undefined;
260
+ }
261
+ /**
262
+ * content metadata, stored with the "content" extension
263
+ *
264
+ * This largely contains description of how to render the document, rather than
265
+ * metadata about it.
266
+ */
267
+ export interface DocumentContent {
268
+ /**
269
+ * which page to use for the thumbnail
270
+ *
271
+ * -1 indicates the last visited page, whereas 0 is the first page.
272
+ */
273
+ coverPageNumber: number;
274
+ /** metadata about the author, publishers, etc. */
275
+ documentMetadata: DocumentMetadata;
276
+ /** It's not known what this field is for */
277
+ dummyDocument?: boolean;
278
+ /** the largely contains metadata about what pens were used and their settings */
279
+ extraMetadata: Record<string, string>;
280
+ /** the underlying file type of this document */
281
+ fileType: FileType;
282
+ /**
283
+ * the name of the font to use for text rendering
284
+ *
285
+ * The reMarkable supports five fonts by default: "Noto Sans", "Noto Sans UI",
286
+ * "EB Garamond", "Noto Mono", and "Noto Serif". You can also set the font to
287
+ * the empty string or omit it for the default.
288
+ */
289
+ fontName: string;
290
+ /** the format version, this should always be 1 */
291
+ formatVersion: number;
292
+ /** the last opened page, starts at zero */
293
+ lastOpenedPage?: number;
294
+ /**
295
+ * the line height
296
+ *
297
+ * The reMarkable uses three built-in line heights: 100, 150, 200, and
298
+ * uses -1 to indicate the default line height, but heights outside of these
299
+ * also work.
300
+ */
301
+ lineHeight: number;
142
302
  /**
143
- * whether to verify the types of the response
303
+ * the document margin in pixels
144
304
  *
145
- * Omitting will make the results always work, but they might nit return
146
- * accurate types anymore.
305
+ * The reMarkable uses three built-in margins: 50, 125, 200, but other margins
306
+ * are possible. The reMarkable used to default to margins of 180.
147
307
  */
148
- verify?: boolean | undefined;
308
+ margins: number;
309
+ /** the document orientation */
310
+ orientation: Orientation;
311
+ /** this specifies the number of pages, it's not clear how this is different than pageCount */
312
+ originalPageCount?: number;
313
+ /** the number of pages */
314
+ pageCount: number;
315
+ /** the page tags for the document */
316
+ pageTags?: PageTag[];
317
+ /** a list of the ids of each page in the document */
318
+ pages?: string[];
319
+ /** a mapping from page number to page id in pages */
320
+ redirectionPageMap?: number[];
321
+ /** ostensibly the size in bytes of the file, but this differs from other measurements */
322
+ sizeInBytes: string;
323
+ /** document tags for this document */
324
+ tags?: Tag[];
325
+ /** text alignment for this document */
326
+ textAlignment: TextAlignment;
327
+ /**
328
+ * the font size
329
+ *
330
+ * reMarkable uses six built-in text scales: 0.7, 0.8, 1, 1.2, 1.5, 2, but
331
+ * values outside of this range are valid.
332
+ */
333
+ textScale: number;
334
+ /** [speculative] the center of the zoom for zoomed in documents */
335
+ customZoomCenterX?: number;
336
+ /** [speculative] the center of the zoom for zoomed in documents */
337
+ customZoomCenterY?: number;
338
+ /** [speculative] the orientation */
339
+ customZoomOrientation?: Orientation;
340
+ /** [speculative] the zoom height for zoomed in pages */
341
+ customZoomPageHeight?: number;
342
+ /** [speculative] the zoom width for zoomed in pages */
343
+ customZoomPageWidth?: number;
344
+ /** [speculative] the scale for zoomed in pages */
345
+ customZoomScale?: number;
346
+ /** what zoom mode is set for the page */
347
+ zoomMode?: ZoomMode;
348
+ /** [speculative] a transform matrix, a. la. css matrix transform */
349
+ transform?: Record<`m${"1" | "2" | "3"}${"1" | "2" | "3"}`, number>;
350
+ /** [speculative] metadata about keyboard use */
351
+ keyboardMetadata?: KeyboardMetadata;
352
+ /** [speculative] various other page metadata */
353
+ cPages?: CPages;
354
+ /**
355
+ * setting for the adaptive contrast filter
356
+ *
357
+ * off has no background filter, best for images, full page applies the high
358
+ * contrast filter to the entire page. If this is omitted, reMarkable will try
359
+ * to apply the filter only to text areas.
360
+ */
361
+ viewBackgroundFilter?: BackgroundFilter;
149
362
  }
150
- export interface UploadOptions extends GetOptions {
151
- /** an optional parent id to set when uploading */
363
+ /** content metadata for any item */
364
+ export type Content = CollectionContent | DocumentContent;
365
+ /**
366
+ * item level metadata
367
+ *
368
+ * Stored with the extension "metadata".
369
+ */
370
+ export interface Metadata {
371
+ /** creation time, a string of the epoch timestamp */
372
+ createdTime?: string;
373
+ /** [speculative] true if the item has been actually deleted */
374
+ deleted?: boolean;
375
+ /** the last modify time, the string of the epoch timestamp */
376
+ lastModified: string;
377
+ /** the last opened epoch timestamp, isn't defined for CollectionType */
378
+ lastOpened?: string;
379
+ /** the last page opened, isn't defined for CollectionType, starts at 0*/
380
+ lastOpenedPage?: number;
381
+ /** [speculative] true if the metadata has been modified */
382
+ metadatamodified?: boolean;
383
+ /** [speculative] true if the item has been modified */
384
+ modified?: boolean;
385
+ /**
386
+ * the id of the parent collection
387
+ *
388
+ * This is the empty string for root (no parent), "trash" if it's in the
389
+ * trash, or the id of the parent.
390
+ */
391
+ parent: string;
392
+ /** true of the item is starred */
393
+ pinned: boolean;
394
+ /** [unknown] */
395
+ synced?: boolean;
396
+ /**
397
+ * the type of item this corresponds to
398
+ *
399
+ * DocumentType is a document, an epub, pdf, or notebook, CollectionType is a
400
+ * folder.
401
+ */
402
+ type: "DocumentType" | "CollectionType";
403
+ /** [speculative] metadata version, always 0 */
404
+ version?: number;
405
+ /** the visible name of the item, what it's called on the reMarkable */
406
+ visibleName: string;
407
+ }
408
+ /**
409
+ * options for putting a file onto reMarkable
410
+ *
411
+ * This is a more customizable version of the options available when using the
412
+ * simpler upload api. This comes with the risk that is uses lower level apis,
413
+ * and therefore has more failure points.
414
+ *
415
+ * @see {@link Content | `Content`} and {@link Metadata | `Metadata`} for more
416
+ * information on what these fields correspond to
417
+ */
418
+ export interface PutOptions {
419
+ /**
420
+ * the collection to put this in
421
+ *
422
+ * The empty string ("") (default) is the root, "trash" is in the trash,
423
+ * otherwise this should be the uuid of a collection item to place this in.
424
+ */
152
425
  parent?: string;
426
+ /** true to star the item */
427
+ pinned?: boolean;
428
+ /** 0 for first page, -1 for last visited */
429
+ coverPageNumber?: number;
430
+ /** document metadata authors */
431
+ authors?: string[];
432
+ /** document metadata tile, NOTE this is not visibleName */
433
+ title?: string;
434
+ /** the publication date, as an ISO date or timestamp */
435
+ publicationDate?: string;
436
+ /** the publisher */
437
+ publisher?: string;
438
+ /** extra metadata often in the form of pen choices */
439
+ extraMetadata?: Record<string, string>;
440
+ /** the font to use for rendering */
441
+ fontName?: string;
442
+ /** the line height to render */
443
+ lineHeight?: number;
444
+ /** the margins to render */
445
+ margins?: number;
446
+ /** the document orientation */
447
+ orientation?: Orientation;
448
+ /** the names of the tags to add */
449
+ tags?: string[];
450
+ /** the document text alignment */
451
+ textAlignment?: TextAlignment;
452
+ /** the text scale of the document */
453
+ textScale?: number;
454
+ /** the document zoom mode */
455
+ zoomMode?: ZoomMode;
456
+ /** the contrast filter setting */
457
+ viewBackgroundFilter?: BackgroundFilter;
458
+ /**
459
+ * whether to refresh current file structure before putting
460
+ *
461
+ * If you suspect that other changes have been made to the remarkable backend
462
+ * between the last put and now, setting this to true will avoid a
463
+ * {@link GenerationError | `GenerationError`}, but will cause an unnecessary
464
+ * GET request otherwise.
465
+ */
466
+ refresh?: boolean;
467
+ }
468
+ /**
469
+ * access to the low-level reMarkable api
470
+ *
471
+ * This class gives more granualar access to the reMarkable cloud, but is more
472
+ * dangerous.
473
+ *
474
+ * ## Overview
475
+ *
476
+ * reMarkable uses an immutable file system, where each file is referenced by
477
+ * the 32 byte sha256 hash of its contents. Each file also has an id used to
478
+ * keep track of updates, so to "update" a file, you upload a new file, and
479
+ * change the hash associated with it's id.
480
+ *
481
+ * Each "item" (a document or a collection) is actually a list of files.
482
+ * The whole reMarkable state is then a list of these lists. Finally, the hash
483
+ * of that list is called the rootHash. To update anything, you have to update
484
+ * the root hash to point to a new list of updated items.
485
+ *
486
+ * This can be dangerous, as corrupting the root hash can destroy all of your
487
+ * files. It is therefore highly recommended to save your current root hash
488
+ * ({@link getRootHash | `getRootHash`}) before using this api to attempt file
489
+ * writes, so you can recover a previous "snapshot" should anything go wrong.
490
+ *
491
+ * ## Items
492
+ *
493
+ * Each item is a collection of individual files. Using
494
+ * {@link getEntries | `getEntries`} on the root hash will give you a list
495
+ * entries that correspond to items. Using `getEntries` on any of those items
496
+ * will get you the files that make up that item.
497
+ *
498
+ * The documented files are:
499
+ * - `<docid>.pdf` - a raw pdf document
500
+ * - `<docid>.epub` - a raw epub document
501
+ * - `<docid>.content` - a json file roughly describing document properties (see {@link DocumentContent | `DocumentContent`})
502
+ * - `<docid>.metadata` - metadata about the document (see {@link Metadata | `Metadata`})
503
+ * - `<docid>.pagedata` - a text file where each line is the template of that page
504
+ * - `<docid>/<pageid>.rm` - [speculative] raw remarkable vectors, text, etc
505
+ * - `<docid>/<pageid>-metadata.json` - [speculative] metadata about the individual page
506
+ * - `<docid>.highlights/<pageid>.json` - [speculative] highlights on the page
507
+ *
508
+ * Some items will have both a `.pdf` and `.epub` file, likely due to preparing
509
+ * for export. Collections only have `.content` and `.metadata` files, with
510
+ * `.content` only containing tags.
511
+ *
512
+ * ## Caching
513
+ *
514
+ * Since everything is tied to the hash of it's contents, we can agressively
515
+ * cache results. We assume that text contents are "small" and so fully cache
516
+ * them, where as binary files we treat as large and only store that we know
517
+ * they exist to prevent future writes.
518
+ *
519
+ * By default, this only persists as long as the api instance is alive. However,
520
+ * for performance reasons, you should call {@link dumpCache | `dumpCache`} to
521
+ * persist the cache between sessions.
522
+ *
523
+ * @remarks
524
+ *
525
+ * Generally all hashes are 64 character hex strings, and all ids are uuid4.
526
+ */
527
+ export interface RawRemarkableApi {
528
+ /**
529
+ * gets the root hash and the current generation
530
+ *
531
+ * When calling `putRootHash`, you should pass the generation you got from
532
+ * this call. That way you tell reMarkable you're updating the previous state.
533
+ *
534
+ * @returns the root hash and the current generation
535
+ */
536
+ getRootHash(): Promise<[string, number]>;
537
+ /**
538
+ * get the raw binary data associated with a hash
539
+ *
540
+ * @param hash - the hash to get the data for
541
+ * @returns the data
542
+ */
543
+ getHash(hash: string): Promise<Uint8Array>;
544
+ /**
545
+ * get raw text data associated with a hash
546
+ *
547
+ * We assume text data are small, and so cache the entire text. If you want to
548
+ * avoid this, use {@link getHash | `getHash`} combined with a TextDecoder.
549
+
550
+ * @param hash - the hash to get text for
551
+ * @returns the text
552
+ */
553
+ getText(hash: string): Promise<string>;
554
+ /**
555
+ * get the entries associated with a list hash
556
+ *
557
+ * A list hash is the root hash, or any hash with the type 80000000. NOTE
558
+ * these are hashed differently than files.
559
+
560
+ * @param hash - the hash to get entries for
561
+ * @returns the entries
562
+ */
563
+ getEntries(hash: string): Promise<RawEntry[]>;
564
+ /**
565
+ * get the parsed and validated `Content` of a content hash
566
+ *
567
+ * Use {@link getText | `getText`} combined with `JSON.parse` to bypass
568
+ * validation
569
+
570
+ * @param hash - the hash to get Content for
571
+ * @returns the content
572
+ */
573
+ getContent(hash: string): Promise<Content>;
574
+ /**
575
+ * get the parsed and validated `Metadata` of a metadata hash
576
+ *
577
+ * Use {@link getText | `getText`} combined with `JSON.parse` to bypass
578
+ * validation
579
+
580
+ * @param hash - the hash to get Metadata for
581
+ * @returns the metadata
582
+ */
583
+ getMetadata(hash: string): Promise<Metadata>;
584
+ /**
585
+ * update the current root hash
586
+ *
587
+ * This will fail if generation doesn't match the current server generation.
588
+ * This ensures that you are updating what you expect. IF you get a
589
+ * {@link GenerationError | `GenerationError`}, that indicates that the server
590
+ * was updated after you last got the generation. You should call
591
+ * {@link getRootHash | `getRootHash`} and then recompute the changes you want
592
+ * from the new root hash. If you ignore the update hash value and just call
593
+ * `putRootHash` again, you will overwrite the changes made by the other
594
+ * update.
595
+ *
596
+ * @param hash - the new root hash
597
+ * @param generation - the generation of the current root hash
598
+ * @param broadcast - [unknown] an option in the request
599
+ *
600
+ * @throws GenerationError if the generation doesn't match the current server generation
601
+ * @returns the new root hash and the new generation
602
+ */
603
+ putRootHash(hash: string, generation: number, broadcast?: boolean): Promise<[string, number]>;
604
+ /**
605
+ * put a raw onto the server
606
+ *
607
+ * This returns the new expeced entry of the file you uploaded, and a promise
608
+ * to finish the upload successful. By splitting these two operations you can
609
+ * start using the uploaded entry while file finishes uploading.
610
+ *
611
+ * NOTE: This won't update the state of the reMarkable until this entry is
612
+ * incorporated into the root hash.
613
+ *
614
+ * @param id - the id of the file to upload
615
+ * @param bytes - the bytes to upload
616
+ * @returns the new entry and a promise to finish the upload
617
+ */
618
+ putFile(id: string, bytes: Uint8Array): Promise<[RawFileEntry, Promise<void>]>;
619
+ /** the same as {@link putFile | `putFile`} but with caching for text */
620
+ putText(id: string, content: string): Promise<[RawFileEntry, Promise<void>]>;
621
+ /** the same as {@link putText | `putText`} but with extra validation for Content */
622
+ putContent(id: string, content: Content): Promise<[RawFileEntry, Promise<void>]>;
623
+ /** the same as {@link putText | `putText`} but with extra validation for Metadata */
624
+ putMetadata(id: string, metadata: Metadata): Promise<[RawFileEntry, Promise<void>]>;
625
+ /**
626
+ * put a set of entries to make an entry list file
627
+ *
628
+ * To fully upload an item:
629
+ * 1. upload all the constituent files and metadata
630
+ * 2. call this with all of the entries
631
+ * 3. append this entry to the root entry and call this again to update this root list
632
+ * 4. put the new root hash
633
+ *
634
+ * @param id - the id of the list to upload - this should be the item id if
635
+ * uploading an item list, or "root" if uploading a new root list.
636
+ * @param entries - the entries to upload
637
+ * @returns the new list entry and a promise to finish the upload
638
+ */
639
+ putEntries(id: string, entries: RawEntry[]): Promise<[RawListEntry, Promise<void>]>;
640
+ /**
641
+ * dump the current cache to a string to preserve between session
642
+ *
643
+ * @returns a serialized version of the cache to pass to a new api instance
644
+ */
645
+ dumpCache(): string;
646
+ /** completely clear the cache */
647
+ clearCache(): void;
153
648
  }
154
649
  /**
155
650
  * the api for accessing remarkable functions
651
+ *
652
+ * There are roughly two types of functions.
653
+ * - high-level api functions that provide simple access with a single round
654
+ * trip based on the web api
655
+ * - low-level wrapped functions that take more round trips, but provide more
656
+ * control and may be faster since they can be cached.
657
+ *
658
+ * Most of these functions validate the return values so that typescript is
659
+ * accurate. However, sometimes those return values are more strict than the
660
+ * "true" underlying types. If this happens, please [submit a an
661
+ * issue](https://github.com/erikbrinkman/rmapi-js/issues). In the mean time,
662
+ * you should be able to use the low level api to work around any restrictive
663
+ * validation.
156
664
  */
157
665
  export interface RemarkableApi {
666
+ /** scoped access to the raw low-level api */
667
+ raw: RawRemarkableApi;
158
668
  /**
159
- * list all files
669
+ * list all items
670
+ *
671
+ * Items include both collections and documents. Documents that are in folders
672
+ * will have their parent set to something other than "" or "trash", but
673
+ * everything will be returned by this function.
160
674
  *
161
675
  * @example
162
676
  * ```ts
163
- * await api.listFiles();
677
+ * await api.listItems();
164
678
  * ```
679
+ *
680
+ * @remarks
681
+ * This is now backed by the low level api, and you may notice some
682
+ * performance degradation if not taking advantage of the cache.
683
+ *
684
+ * @param refresh - if true, refresh the root hash before listing
685
+ * @returns a list of all items with some metadata
686
+ */
687
+ listItems(refresh?: boolean): Promise<Entry[]>;
688
+ /**
689
+ * similar to {@link listItems | `listItems`} but backed by the low level api
690
+ *
691
+ * @param refresh - if true, refresh the root hash before listing
692
+ */
693
+ listIds(refresh?: boolean): Promise<SimpleEntry[]>;
694
+ /**
695
+ * get the content metadata from an item hash
696
+ *
697
+ * This takes the high level item hash, e.g. the hashes you get from
698
+ * {@link listItems | `listItems`} or {@link listIds | `listIds`}.
699
+ *
700
+ * @remarks
701
+ * If this fails validation and you still want to get the content, you can use
702
+ * the low-level api to get the raw text of the `.content` file in the
703
+ * `RawListEntry` for this hash.
704
+ *
705
+ * @param hash - the hash of the item to get content for
706
+ * @returns the content
165
707
  */
166
- listFiles(ops?: GetOptions): Promise<Entry[]>;
708
+ getContent(hash: string): Promise<Content>;
709
+ /**
710
+ * get the metadata from an item hash
711
+ *
712
+ * This takes the high level item hash, e.g. the hashes you get from
713
+ * {@link listItems | `listItems`} or {@link listIds | `listIds`}.
714
+ *
715
+ * @remarks
716
+ * If this fails validation and you still want to get the content, you can use
717
+ * the low-level api to get the raw text of the `.metadata` file in the
718
+ * `RawListEntry` for this hash.
719
+ *
720
+ * @param hash - the hash of the item to get metadata for
721
+ * @returns the metadata
722
+ */
723
+ getMetadata(hash: string): Promise<Metadata>;
724
+ /**
725
+ * get the pdf associated with a document hash
726
+ *
727
+ * This returns the raw input pdf, not the rendered pdf with any markup.
728
+ *
729
+ * @param hash - the hash of the document to get the pdf for (e.g. the hash
730
+ * received from `listItems`)
731
+ * @returns the pdf bytes
732
+ */
733
+ getPdf(hash: string): Promise<Uint8Array>;
734
+ /**
735
+ * get the epub associated with a document hash
736
+ *
737
+ * This returns the raw input epub if a document was created from an epub.
738
+ *
739
+ * @param hash - the hash of the document to get the pdf for (e.g. the hash
740
+ * received from `listItems`)
741
+ * @returns the epub bytes
742
+ */
743
+ getEpub(hash: string): Promise<Uint8Array>;
744
+ /**
745
+ * get the entire contents of a remarkable document
746
+ *
747
+ * This gets every file of associated with a document, and puts them into a
748
+ * zip archive.
749
+ *
750
+ * @remarks
751
+ * This is an experimental feature, that works for downloading the raw version
752
+ * of the document, but this format isn't understood enoguh to reput this on a
753
+ * different remarkable, so that functionality is currently disabled.
754
+ *
755
+ * @param hash - the hash of the document to get the contents for (e.g. the
756
+ * hash received from `listItems`)
757
+ */
758
+ getDocument(hash: string): Promise<Uint8Array>;
759
+ /**
760
+ * use the low-level api to add a pdf document
761
+ *
762
+ * Since this uses the low-level api, it provides more options than
763
+ * {@link uploadPdf | `uploadPdf`}, but is a little more finicky. Notably, it
764
+ * may throw a {@link GenerationError | `GenerationError`} if the generation
765
+ * doesn't match the current server generation, requiring you to retry until
766
+ * it works.
767
+ *
768
+ * @param visibleName - the name to display on the reMarkable
769
+ * @param buffer - the raw pdf
770
+ * @param opts - put options
771
+ * @throws GenerationError if the generation doesn't match the current server generation
772
+ * @returns the entry for the newly inserted document
773
+ */
774
+ putPdf(visibleName: string, buffer: Uint8Array, opts?: PutOptions): Promise<SimpleEntry>;
775
+ /**
776
+ * use the low-level api to add an epub document
777
+ *
778
+ * Since this uses the low-level api, it provides more options than
779
+ * {@link uploadEpub | `uploadEpub`}, but is a little more finicky. Notably, it
780
+ * may throw a {@link GenerationError | `GenerationError`} if the generation
781
+ * doesn't match the current server generation, requiring you to retry until
782
+ * it works.
783
+ *
784
+ * @param visibleName - the name to display on the reMarkable
785
+ * @param buffer - the raw epub
786
+ * @param opts - put options
787
+ * @throws GenerationError if the generation doesn't match the current server generation
788
+ * @returns the entry for the newly inserted document
789
+ */
790
+ putEpub(visibleName: string, buffer: Uint8Array, opts?: PutOptions): Promise<SimpleEntry>;
167
791
  /** create a folder */
168
- createFolder(visibleName: string, opts?: UploadOptions): Promise<UploadEntry>;
792
+ createFolder(visibleName: string, opts?: UploadOptions, refresh?: boolean): Promise<SimpleEntry>;
169
793
  /**
170
794
  * upload an epub
171
795
  *
@@ -174,10 +798,13 @@ export interface RemarkableApi {
174
798
  * await api.uploadEpub("My EPub", ...);
175
799
  * ```
176
800
  *
801
+ * @remarks
802
+ * this is now simply a less powerful version of {@link putEpub | `putEpub`}.
803
+ *
177
804
  * @param visibleName - the name to show for the uploaded epub
178
805
  * @param buffer - the epub contents
179
806
  */
180
- uploadEpub(visibleName: string, buffer: ArrayBuffer, opts?: UploadOptions): Promise<UploadEntry>;
807
+ uploadEpub(visibleName: string, buffer: Uint8Array, opts?: UploadOptions): Promise<SimpleEntry>;
181
808
  /**
182
809
  * upload a pdf
183
810
  *
@@ -186,10 +813,13 @@ export interface RemarkableApi {
186
813
  * await api.uploadPdf("My PDF", ...);
187
814
  * ```
188
815
  *
816
+ * @remarks
817
+ * this is now simply a less powerful version of {@link putPdf | `putPdf`}.
818
+ *
189
819
  * @param visibleName - the name to show for the uploaded epub
190
820
  * @param buffer - the epub contents
191
821
  */
192
- uploadPdf(visibleName: string, buffer: ArrayBuffer, opts?: UploadOptions): Promise<UploadEntry>;
822
+ uploadPdf(visibleName: string, buffer: Uint8Array, opts?: UploadOptions): Promise<SimpleEntry>;
193
823
  /**
194
824
  * move an entry
195
825
  *
@@ -201,7 +831,7 @@ export interface RemarkableApi {
201
831
  * @param hash - the hash of the file to move
202
832
  * @param parent - the id of the directory to move the entry to, "" (root) and "trash" are special parents
203
833
  */
204
- move(hash: string, parent: string, opts?: GetOptions): Promise<HashEntry>;
834
+ move(hash: string, parent: string, refresh?: boolean): Promise<HashEntry>;
205
835
  /**
206
836
  * delete an entry
207
837
  *
@@ -211,7 +841,7 @@ export interface RemarkableApi {
211
841
  * ```
212
842
  * @param hash - the hash of the entry to delete
213
843
  */
214
- delete(hash: string, opts?: GetOptions): Promise<HashEntry>;
844
+ delete(hash: string, refresh?: boolean): Promise<HashEntry>;
215
845
  /**
216
846
  * rename an entry
217
847
  *
@@ -222,7 +852,7 @@ export interface RemarkableApi {
222
852
  * @param hash - the hash of the entry to rename
223
853
  * @param visibleName - the new name to assign
224
854
  */
225
- rename(hash: string, visibleName: string, opts?: GetOptions): Promise<HashEntry>;
855
+ rename(hash: string, visibleName: string, refresh?: boolean): Promise<HashEntry>;
226
856
  /**
227
857
  * move many entries
228
858
  *
@@ -234,7 +864,7 @@ export interface RemarkableApi {
234
864
  * @param hashes - an array of entry hashes to move
235
865
  * @param parent - the directory id to move the entries to, "" (root) and "trash" are special ids
236
866
  */
237
- bulkMove(hashes: readonly string[], parent: string, opts?: GetOptions): Promise<HashesEntry>;
867
+ bulkMove(hashes: readonly string[], parent: string, refresh?: boolean): Promise<HashesEntry>;
238
868
  /**
239
869
  * delete many entries
240
870
  *
@@ -245,23 +875,40 @@ export interface RemarkableApi {
245
875
  *
246
876
  * @param hashes - the hashes of the entries to delete
247
877
  */
248
- bulkDelete(hashes: readonly string[], opts?: GetOptions): Promise<HashesEntry>;
249
- }
250
- /** options for a remarkable instance */
251
- export interface RemarkableOptions {
878
+ bulkDelete(hashes: readonly string[], refresh?: boolean): Promise<HashesEntry>;
879
+ /**
880
+ * get the current cache value as a string
881
+ *
882
+ * You can use this to warm start a new instance of
883
+ * {@link remarkable | `remarkable`} with any previously cached results.
884
+ */
885
+ dumpCache(): string;
252
886
  /**
253
- * the fetch method to use
887
+ * prune the cache so that it contains only reachable hashes
888
+ *
889
+ * The cache is append only, so it can grow without bound, even as hashes
890
+ * become unreachable. In the future, this may have better cache management to
891
+ * track this in real time, but for now, you can call this method, to keep it
892
+ * from growing continuously.
254
893
  *
255
- * This should loosely conform to the WHATWG fetch, but is relaxed enough that
256
- * node-fetch also works. This will default to the global definitions of
257
- * fetch.
894
+ * @remarks
895
+ * This won't necessarily reduce the cache size. In order to see if
896
+ * hashes are reachable we first have to search through all existing entry
897
+ * lists.
258
898
  *
259
- * In node you can either use `"node-fetch"`, or `node --experimental-fetch`
260
- * for node 17.5 or higher.
899
+ * @param refresh - whether to refresh the root hash before pruning
900
+ */
901
+ pruneCache(refresh?: boolean): Promise<void>;
902
+ /**
903
+ * completely delete the cache
261
904
  *
262
- * @defaultValue globalThis.fetch
905
+ * If the cache is causing memory issues, you can clear it, but this will hurt
906
+ * performance.
263
907
  */
264
- fetch?: FetchLike;
908
+ clearCache(): void;
909
+ }
910
+ /** options for a remarkable instance */
911
+ export interface RemarkableOptions {
265
912
  /**
266
913
  * the url for making authorization requests
267
914
  *
@@ -271,9 +918,32 @@ export interface RemarkableOptions {
271
918
  /**
272
919
  * the url for making synchronization requests
273
920
  *
274
- * @defaultValue "https://internal.cloud.remarkable.com"
921
+ * @defaultValue "https://web.eu.tectonic.remarkable.com"
275
922
  */
276
923
  syncHost?: string;
924
+ /**
925
+ * the url for making requests using the low-level api
926
+ *
927
+ * @defaultValue "https://eu.tectonic.remarkable.com"
928
+ */
929
+ rawHost?: string;
930
+ /**
931
+ * an initial cache value
932
+ *
933
+ * Generated from calling {@link RemarkableApi.dumpCache | `dumpCache`} on a previous
934
+ * instance.
935
+ */
936
+ cache?: string;
937
+ /**
938
+ * the maximum size of the cache in terms of total string length
939
+ *
940
+ * By the JavaScript specification there are two bytes per character, but the
941
+ * total memory usage of the cache will also be larger than just the size of
942
+ * the data stored.
943
+ *
944
+ * @defaultValue Infinity
945
+ */
946
+ maxCacheSize?: number;
277
947
  }
278
948
  /**
279
949
  * create an instance of the api
@@ -285,4 +955,4 @@ export interface RemarkableOptions {
285
955
  * registered. Create one with {@link register}.
286
956
  * @returns an api instance
287
957
  */
288
- export declare function remarkable(deviceToken: string, { fetch, authHost, syncHost, }?: RemarkableOptions): Promise<RemarkableApi>;
958
+ export declare function remarkable(deviceToken: string, { authHost, rawHost, cache, maxCacheSize, }?: RemarkableOptions): Promise<RemarkableApi>;