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