rmapi-js 5.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
@@ -1,240 +1,134 @@
1
1
  /** request types */
2
- export type RequestMethod = "POST" | "GET" | "PUT" | "DELETE";
3
- /** text alignment types */
2
+ export type RequestMethod = "POST" | "GET" | "PUT" | "DELETE" | "PATCH" | "OPTIONS";
3
+ /** a tag for an entry */
4
+ export interface Tag {
5
+ /** the name of the tag */
6
+ name: string;
7
+ /** the timestamp when this tag was added */
8
+ timestamp: number;
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 */
4
20
  export type TextAlignment = "justify" | "left";
5
- /** tool options */
6
- export declare const builtinTools: readonly ["Ballpoint", "Ballpointv2", "Brush", "Calligraphy", "ClearPage", "EraseSection", "Eraser", "Fineliner", "Finelinerv2", "Highlighter", "Highlighterv2", "Marker", "Markerv2", "Paintbrush", "Paintbrushv2", "Pencilv2", "SharpPencil", "SharpPencilv2", "SolidPen", "ZoomTool"];
7
- /** font name options */
8
- export declare const builtinFontNames: readonly ["Maison Neue", "EB Garamond", "Noto Sans", "Noto Serif", "Noto Mono", "Noto Sans UI"];
9
- /** text scale options */
10
- export declare const builtinTextScales: {
11
- /** the smallest */
12
- readonly xs: 0.7;
13
- /** small */
14
- readonly sm: 0.8;
15
- /** medium / default */
16
- readonly md: 1;
17
- /** large */
18
- readonly lg: 1.2;
19
- /** extra large */
20
- readonly xl: 1.5;
21
- /** double extra large */
22
- readonly xx: 2;
23
- };
24
- /** margin options */
25
- export declare const builtinMargins: {
26
- /** small */
27
- readonly sm: 50;
28
- /** medium */
29
- readonly md: 125;
30
- /** default for read on remarkable */
31
- readonly rr: 180;
32
- /** large */
33
- readonly lg: 200;
34
- };
35
- /** line height options */
36
- export declare const builtinLineHeights: {
37
- /** default */
38
- readonly df: -1;
39
- /** normal */
40
- readonly md: 100;
41
- /** half */
42
- readonly lg: 150;
43
- /** double */
44
- readonly xl: 200;
45
- };
46
- /** a remarkable entry for a cloud collection */
47
- export interface CollectionEntry {
48
- /** collection type */
49
- type: "80000000";
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;
50
42
  /** the hash of the collection this points to */
51
43
  hash: string;
52
44
  /** the unique id of the collection */
53
- documentId: string;
45
+ id: string;
54
46
  /** the number of subfiles */
55
47
  subfiles: number;
56
- /** collections don't have sizes */
57
- size: bigint;
48
+ /** the total size of everything in the collection */
49
+ size: number;
58
50
  }
59
- /** a remarkable entry for cloud data */
60
- export interface FileEntry {
61
- /** file type */
62
- type: "0";
51
+ /** the low-level entry for a single file */
52
+ export interface RawFileEntry {
53
+ /** file type (0) */
54
+ type: 0;
63
55
  /** the hash of the file this points to */
64
56
  hash: string;
65
57
  /** the unique id of the file */
66
- documentId: string;
67
- /** files don't have subfiles */
58
+ id: string;
59
+ /** the number of subfiles, always zero */
68
60
  subfiles: 0;
69
- /** size of the file */
70
- size: bigint;
61
+ /** the size of the file in bytes */
62
+ size: number;
71
63
  }
72
- /** a remarkable entry for cloud items */
73
- export type Entry = CollectionEntry | FileEntry;
74
- /** an simple entry produced by the upload api */
75
- export interface UploadEntry {
76
- /** the document id */
77
- docID: string;
78
- /** the document hash */
64
+ /** a low-level stored entry */
65
+ export type RawEntry = RawListEntry | RawFileEntry;
66
+ /** common properties shared by collections and documents */
67
+ export interface EntryCommon {
68
+ /** the document id, a uuid4 */
69
+ id: string;
70
+ /** the current hash of the state of this entry */
79
71
  hash: string;
80
- }
81
- /** common metadata for documents and collections */
82
- export interface CommonMetadata {
83
- /** name of content */
72
+ /** the visible display name of this entry */
84
73
  visibleName: string;
85
- /** parent uuid (documentId) or "" for root or "trash" */
74
+ /** the last modified timestamp */
75
+ lastModified: string;
76
+ /** true if the entry is starred in most ui elements */
77
+ pinned: boolean;
78
+ /**
79
+ * the parent of this entry
80
+ *
81
+ * There are two special parents, "" (empty string) for the root directory,
82
+ * and "trash" for the trash
83
+ */
86
84
  parent?: string;
87
- /** last modified time */
88
- lastModified?: string;
89
- /** unknown significance */
90
- version?: number;
91
- /** unknown significance */
92
- pinned?: boolean;
93
- /** unknown significance */
94
- synced?: boolean;
95
- /** unknown significance */
96
- modified?: boolean;
97
- /** if file is deleted */
98
- deleted?: boolean;
99
- /** unknown significance */
100
- metadatamodified?: boolean;
85
+ /** any tags the entry might have */
86
+ tags?: Tag[];
101
87
  }
102
- /** metadata for collection types */
103
- export interface CollectionTypeMetadata extends CommonMetadata {
104
- /** the key for collection types */
88
+ /** a folder, referred to in the api as a collection */
89
+ export interface CollectionEntry extends EntryCommon {
90
+ /** the key for this as a collection */
105
91
  type: "CollectionType";
106
92
  }
107
- /** metadata for document types */
108
- export interface DocumentTypeMetadata extends CommonMetadata {
109
- /** the key for document types */
93
+ /** a file, referred to in the api as a document */
94
+ export interface DocumentType extends EntryCommon {
95
+ /** the key to identify this as a document */
110
96
  type: "DocumentType";
111
- /** last opened time for documents */
112
- lastOpened?: string;
113
- /** last opened page for documents */
114
- lastOpenedPage?: number;
115
- /** created time */
116
- createdTime?: string;
97
+ /** the type of the file */
98
+ fileType: "epub" | "pdf" | "notebook";
99
+ /** the timestamp of the last time this entry was opened */
100
+ lastOpened: string;
117
101
  }
118
- /**
119
- * metadata for a document or collection (folder)
120
- *
121
- * This is found in the the `.metadata` file.
122
- */
123
- export type Metadata = CollectionTypeMetadata | DocumentTypeMetadata;
124
- /** fields common to all {@link MetadataEntry | MetadataEntries} */
125
- export interface BaseMetadataEntry {
126
- /** the document id of the entry */
102
+ /** a remarkable entry for cloud items */
103
+ export type Entry = CollectionEntry | DocumentType;
104
+ /** an simple entry without any extra information */
105
+ export interface SimpleEntry {
106
+ /** the document id */
127
107
  id: string;
128
- /** the hash of the entry */
108
+ /** the document hash */
129
109
  hash: string;
130
110
  }
131
- /** the metadata entry for a collection */
132
- export interface CollectionMetadataEntry extends BaseMetadataEntry, CollectionTypeMetadata {
111
+ /** the new hash of a modified entry */
112
+ export interface HashEntry {
113
+ /** the actual hash */
114
+ hash: string;
133
115
  }
134
- /** the metadata entry for a document */
135
- export interface DocumentMetadataEntry extends BaseMetadataEntry, DocumentTypeMetadata {
136
- /** the type of the document */
137
- fileType: FileType;
116
+ /** the mapping from old hashes to new hashes after a bulk modify */
117
+ export interface HashesEntry {
118
+ /** the mapping from old to new hashes */
119
+ hashes: Record<string, string>;
138
120
  }
139
- /** a full metadata entry returned by {@link RemarkableApi.getEntriesMetadata} */
140
- export type MetadataEntry = CollectionMetadataEntry | DocumentMetadataEntry;
141
- /** extra content metadata */
142
- export type ExtraMetadata = Record<string, string | undefined>;
143
- /** remarkable file type; empty for notebook */
144
- export type FileType = "notebook" | "pdf" | "epub" | "";
145
- /** document matrix transform */
146
- export type Transform = Record<`m${1 | 2 | 3}${1 | 2 | 3}`, number>;
147
- /** content document metadata */
148
- export interface DocumentMetadata {
149
- /** document title */
150
- title?: string;
151
- /** document authors */
152
- authors?: string[];
153
- }
154
- /**
155
- * content metadata
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.
156
127
  *
157
- * This is found in the `.content` file.
128
+ * @see {@link RawRemarkableApi | `RawRemarkableApi`}
158
129
  */
159
- export interface Content {
160
- /** is this a dummy document */
161
- dummyDocument: boolean;
162
- /** document metadata */
163
- documentMetadata?: DocumentMetadata;
164
- /** extra metadata */
165
- extraMetadata: ExtraMetadata;
166
- /** file type */
167
- fileType: FileType;
168
- /** font */
169
- fontName?: string;
170
- /** last opened page */
171
- lastOpenedPage: number;
172
- /** line height */
173
- lineHeight: number;
174
- /** page margins in points */
175
- margins: number;
176
- /** orientation */
177
- orientation?: "portrait" | "landscape";
178
- /** number of pages */
179
- pageCount: number;
180
- /** page ids */
181
- pages: string[];
182
- /** number to use for the coverage page, -1 for last opened */
183
- coverPageNumber: number;
184
- /** text scale */
185
- textScale: number;
186
- /** page transform */
187
- transform?: Transform;
188
- /** format version */
189
- formatVersion: number;
190
- /** text alignment */
191
- textAlignment?: TextAlignment;
192
- }
193
- /** stripped down version of RequestInit */
194
- export interface RequestInitLike {
195
- /** request method */
196
- readonly method?: RequestMethod | undefined;
197
- /** request headers */
198
- readonly headers?: Record<string, string>;
199
- /** request body */
200
- readonly body?: ArrayBuffer | string;
201
- }
202
- /** stripped down version of Headers */
203
- export interface HeadersLike {
204
- /** get a specific header value */
205
- get(key: string): string | null;
206
- }
207
- /** stripped down version of Response */
208
- export interface ResponseLike {
209
- /** true if request was successful */
210
- ok: boolean;
211
- /** http status */
212
- status: number;
213
- /** text associated with status */
214
- statusText: string;
215
- /** headers in response */
216
- headers: HeadersLike;
217
- /** get response body as text */
218
- text(): Promise<string>;
219
- /** get response body as an array buffer */
220
- arrayBuffer(): Promise<ArrayBuffer>;
221
- }
222
- /** stripped down version of fetch */
223
- export interface FetchLike {
224
- /** the rough interface to fetch */
225
- (url: string, options?: RequestInitLike | undefined): Promise<ResponseLike>;
226
- }
227
- /** async storage, map like */
228
- export interface CacheLike {
229
- /** get value for key or undefined if missing */
230
- get(key: string): Promise<string | undefined>;
231
- /** set value for key */
232
- set(key: string, value: string): Promise<void>;
233
- }
234
- /** stripped down version of subtle crypto */
235
- export interface SubtleCryptoLike {
236
- /** a digest function */
237
- digest(algorithm: "SHA-256", data: BufferSource): Promise<ArrayBuffer>;
130
+ export declare class GenerationError extends Error {
131
+ constructor();
238
132
  }
239
133
  /** an error that results from a failed request */
240
134
  export declare class ResponseError extends Error {
@@ -244,14 +138,13 @@ export declare class ResponseError extends Error {
244
138
  readonly statusText: string;
245
139
  constructor(status: number, statusText: string, message: string);
246
140
  }
247
- /**
248
- * an error that results from trying yp update the wrong generation.
249
- *
250
- * If we try to update the root hash of files, but the generation has changed
251
- * relative to the one we're updating from, this will fail.
252
- */
253
- export declare class GenerationError extends Error {
254
- constructor();
141
+ /** an error that results from a failed request */
142
+ export declare class ValidationError extends Error {
143
+ /** the response status number */
144
+ readonly field: string;
145
+ /** the response status text */
146
+ readonly regex: RegExp;
147
+ constructor(field: string, regex: RegExp, message: string);
255
148
  }
256
149
  /** options for registering with the api */
257
150
  export interface RegisterOptions {
@@ -268,8 +161,6 @@ export interface RegisterOptions {
268
161
  uuid?: string;
269
162
  /** The host to use for authorization requests */
270
163
  authHost?: string;
271
- /** a function for making fetch requests, see {@link RemarkableOptions.fetch} for more info */
272
- fetch?: FetchLike;
273
164
  }
274
165
  /**
275
166
  * register a device and get the token needed to access the api
@@ -281,312 +172,616 @@ export interface RegisterOptions {
281
172
  * @param code - the eight letter code a user got from `https://my.remarkable.com/device/browser/connect`.
282
173
  * @returns the device token necessary for creating an api instace. These never expire so persist as long as necessary.
283
174
  */
284
- export declare function register(code: string, { deviceDesc, uuid, authHost, fetch, }?: RegisterOptions): Promise<string>;
285
- /** options for uploading an epub document */
286
- export interface PutEpubOptions {
287
- /** the parent id, default to root */
288
- parent?: string | undefined;
289
- /** the margins of the epub 180 is good for articles, 125 for books */
290
- margins?: number | keyof typeof builtinMargins | undefined;
291
- /** the height of lines */
292
- lineHeight?: number | keyof typeof builtinLineHeights | undefined;
293
- /** the scale of text */
294
- textScale?: number | keyof typeof builtinTextScales | undefined;
295
- /** the page orientation */
296
- orientation?: "portrait" | "landscape" | undefined;
297
- /** the text alignment */
298
- textAlignment?: TextAlignment | undefined;
299
- /** which page should be shone as the cover */
300
- cover?: "first" | "visited" | undefined;
301
- /** the font name, should probably come from `builtinFontNames` */
302
- fontName?: string | undefined;
303
- /** the tool to have enabled by default */
304
- lastTool?: string;
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;
305
254
  }
306
255
  /**
307
- * options for {@link RemarkableApi.getRootHash | `getRootHash`}
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.
308
260
  */
309
- export interface GetRootHashOptions {
261
+ export interface DocumentContent {
310
262
  /**
311
- * whether to use the last known valid root hash
263
+ * which page to use for the thumbnail
312
264
  *
313
- * @defaultValue true
265
+ * -1 indicates the last visited page, whereas 0 is the first page.
314
266
  */
315
- cache?: boolean;
316
- }
317
- /**
318
- * options for {@link RemarkableApi.create | `create`} and
319
- * {@link RemarkableApi.move | `move`}
320
- */
321
- export interface CreateMoveOptions {
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;
322
288
  /**
323
- * whether to use the last known valid root hash
289
+ * the line height
324
290
  *
325
- * @defaultValue true
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.
326
294
  */
327
- cache?: boolean;
295
+ lineHeight: number;
328
296
  /**
329
- * whether broadcast syncing complete to other devices
297
+ * the document margin in pixels
330
298
  *
331
- * @defaultValue true
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.
332
301
  */
333
- sync?: boolean;
334
- }
335
- /** options for uploading a pdf */
336
- export interface PutPdfOptions {
337
- /** the parent id, default to root */
338
- parent?: string | undefined;
339
- /** the page orientation */
340
- orientation?: "portrait" | "landscape" | undefined;
341
- /** which page should be shone as the cover */
342
- cover?: "first" | "visited" | undefined;
343
- /** the tool to have enabled by default */
344
- lastTool?: string;
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;
345
356
  }
346
- /** options for getting responses */
347
- export interface GetOptions {
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;
348
379
  /**
349
- * whether to verify the types of the response
380
+ * the id of the parent collection
350
381
  *
351
- * Omitting will make the results always work, but they might nit return
352
- * accurate types anymore.
382
+ * This is the empty string for root (no parent), "trash" if it's in the
383
+ * trash, or the id of the parent.
353
384
  */
354
- verify?: boolean | undefined;
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;
355
401
  }
356
402
  /**
357
- * the api for accessing remarkable functions
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
358
411
  */
359
- export interface RemarkableApi {
412
+ export interface PutOptions {
360
413
  /**
361
- * get the root hash and the current generation
414
+ * the collection to put this in
362
415
  *
363
- * If this hasn't changed, then neither have any of the files.
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.
364
418
  */
365
- getRootHash(options?: GetRootHashOptions): Promise<[string, bigint]>;
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;
366
452
  /**
367
- * write the root hash, incrimenting from the current generation
453
+ * whether to refresh current file structure before putting
368
454
  *
369
- * This will fail if the current generation isn't equal to the passed in
370
- * generation. Use this to preven race conditions. If this rejects, refetch
371
- * the root hash, resync the updates, and then try to put again.
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
372
524
  *
373
- * @remarks Updating the root hash can be dangerous as it changes the full
374
- * state of what's on the the remarkable. However, it's also the only way to
375
- * change what's actually visible. If you're unsure if you're doing the right
376
- * thing, make sure to first save your inital root hash. Then you can always
377
- * recover by doing:
378
- * ```
379
- * let [, gen] = await api.getRootHash();
380
- * await api.putRootHash(backup, gen);
381
- * ```
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.
382
527
  *
383
- * @param hash - the hash of the new root collection
384
- * @param generation - the current generation this builds off of
385
- * @returns the new generation
386
- * @throws {@link GenerationError} if the current cloud generation didn't
387
- * match the expected pushed generation.
528
+ * @returns the root hash and the current generation
388
529
  */
389
- putRootHash(hash: string, generation: bigint): Promise<bigint>;
530
+ getRootHash(): Promise<[string, number]>;
390
531
  /**
391
- * get array buffer associated with a hash
532
+ * get the raw binary data associated with a hash
392
533
  *
393
- * @param hash - the hash to get text data from
534
+ * @param hash - the hash to get the data for
535
+ * @returns the data
394
536
  */
395
- getBuffer(hash: string): Promise<ArrayBuffer>;
537
+ getHash(hash: string): Promise<Uint8Array>;
396
538
  /**
397
- * get text content associated with hash
539
+ * get raw text data associated with a hash
398
540
  *
399
- * @param hash - the hash to get text data from
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
400
546
  */
401
547
  getText(hash: string): Promise<string>;
402
548
  /**
403
- * get a json object associated with a hash
549
+ * get the entries associated with a list hash
404
550
  *
405
- * This is identical to `getText(hash).then(JSON.parse)` and is only provided
406
- * for consistency with {@link RemarkableApi.putJson | `putJson`}.
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
407
556
  */
408
- getJson(hash: string): Promise<unknown>;
409
- /** get metadata from hash */
410
- getMetadata(hash: string, opts?: GetOptions): Promise<Metadata>;
557
+ getEntries(hash: string): Promise<RawEntry[]>;
411
558
  /**
412
- * get entries from a collection hash
559
+ * get the parsed and validated `Content` of a content hash
413
560
  *
414
- * If omitted, this will use `getRootHash()`.
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
415
566
  */
416
- getEntries(hash?: string): Promise<Entry[]>;
417
- /** put a reference to a set of entries into the cloud */
418
- putEntries(documentId: string, entries: Entry[]): Promise<CollectionEntry>;
419
- /** put a raw buffer in the cloud */
420
- putBuffer(documentId: string, buffer: ArrayBuffer): Promise<FileEntry>;
567
+ getContent(hash: string): Promise<Content>;
421
568
  /**
422
- * put a raw text in the cloud encoded as utf-8
569
+ * get the parsed and validated `Metadata` of a metadata hash
423
570
  *
424
- * this is no different than using `putBuffer(..., new TextEncoder().encode(contents))`
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
425
576
  */
426
- putText(documentId: string, contents: string): Promise<FileEntry>;
577
+ getMetadata(hash: string): Promise<Metadata>;
427
578
  /**
428
- * put json into the cloud
429
- *
430
- * This uses a stable (sorted keys) json serilization to preserve consistent
431
- * hashes.
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
432
596
  */
433
- putJson(documentId: string, contents: object): Promise<FileEntry>;
597
+ putRootHash(hash: string, generation: number, broadcast?: boolean): Promise<[string, number]>;
434
598
  /**
435
- * put metadata into the cloud
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.
436
604
  *
437
- * This is a small wrapper around {@link RemarkableApi.putText | `putText`}.
605
+ * NOTE: This won't update the state of the reMarkable until this entry is
606
+ * incorporated into the root hash.
438
607
  *
439
- * @param documentId - this should be the documentId of the item that this is
440
- * metadata for; it should not end in `.metadata`
441
- * @param metadata - the metadata to upload
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
442
611
  */
443
- putMetadata(documentId: string, metadata: Metadata): Promise<FileEntry>;
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>]>;
444
619
  /**
445
- * create a new collection (folder)
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
446
636
  *
447
- * @param visibleName - the name of the new folder
448
- * @param parent - the documentId of the parent collection (folder)
637
+ * @returns a serialized version of the cache to pass to a new api instance
449
638
  */
450
- putCollection(visibleName: string, parent?: string): Promise<CollectionEntry>;
639
+ dumpCache(): string;
640
+ /** completely clear the cache */
641
+ clearCache(): void;
642
+ }
643
+ /**
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.
658
+ */
659
+ export interface RemarkableApi {
660
+ /** scoped access to the raw low-level api */
661
+ raw: RawRemarkableApi;
451
662
  /**
452
- * upload an epub
663
+ * list all items
453
664
  *
454
- * @remarks this only uploads the raw data and returns an entry that could be
455
- * uploaded as part of a larger collection. To make sure devices know about
456
- * it, you'll still need to update the root hash, and potentially notify
457
- * other devices.
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.
458
668
  *
459
669
  * @example
460
- * This example shows a full process upload. Note that it may fail if other
461
- * devices are simultaneously syncing content. See {@link putRootHash} for
462
- * more information.
463
- *
464
670
  * ```ts
465
- * const entry = await api.putEpub(...);
466
- * await api.create(entry);
671
+ * await api.listItems();
467
672
  * ```
468
673
  *
469
- * @param visibleName - the name to show for the uploaded epub
470
- * @param buffer - the epub contents
471
- * @param opts - extra options you can specify at upload
674
+ * @returns a list of all items with some metadata
472
675
  */
473
- putEpub(visibleName: string, buffer: ArrayBuffer, opts?: PutEpubOptions): Promise<CollectionEntry>;
676
+ listItems(): Promise<Entry[]>;
474
677
  /**
475
- * upload a pdf
678
+ * similar to {@link listItems | `listItems`} but backed by the low level api
476
679
  *
477
- * @remarks this only uploads the raw data and returns an entry that could be
478
- * uploaded as part of a larger collection. To make sure devices know about
479
- * it, you'll still need to update the root hash, and potentially notify
480
- * other devices.
680
+ * @param refresh - if true, refresh the root hash before listing
681
+ */
682
+ listIds(refresh?: boolean): Promise<SimpleEntry[]>;
683
+ /**
684
+ * get the content metadata from an item hash
481
685
  *
482
- * @example
483
- * This example shows a full process upload. Note that it may fail if other
484
- * devices are simultaneously syncing content. See {@link putRootHash} for
485
- * more information.
686
+ * This takes the high level item hash, e.g. the hashes you get from
687
+ * {@link listItems | `listItems`} or {@link listIds | `listIds`}.
486
688
  *
487
- * ```ts
488
- * const entry = await api.putPdf(...);
489
- * await api.create(entry);
490
- * ```
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.
491
693
  *
492
- * @param visibleName - the name to show for the uploaded pdf
493
- * @param buffer - the pdf contents
494
- * @param opts - extra options you can specify at upload
694
+ * @param hash - the hash of the item to get content for
695
+ * @returns the content
495
696
  */
496
- putPdf(visibleName: string, buffer: ArrayBuffer, opts?: PutPdfOptions): Promise<CollectionEntry>;
697
+ getContent(hash: string): Promise<Content>;
497
698
  /**
498
- * indicate that a sync is complete and push updates to other devices
699
+ * get the metadata from an item hash
499
700
  *
500
- * @remarks after successfully putting a new root hash, use this to indicate
501
- * that other devices should pick up the change.
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
502
711
  */
503
- syncComplete(generation: bigint): Promise<void>;
712
+ getMetadata(hash: string): Promise<Metadata>;
504
713
  /**
505
- * high level api to create an entry
714
+ * get the pdf associated with a document hash
506
715
  *
507
- * After creating a collection entry with
508
- * {@link RemarkableApi.putCollection | `putCollection`},
509
- * {@link RemarkableApi.putEpub | `putEpub`}, or
510
- * {@link RemarkableApi.putPdf | `putPdf`}, this is a high level API to try
511
- * syncing the change to the remarkable.
716
+ * This returns the raw input pdf, not the rendered pdf with any markup.
512
717
  *
513
- * @remarks
514
- * This API is provided to give a high level interface and to serve as an
515
- * example of how to implement more advanced functionality, but it comes with
516
- * a number of caveats:
517
- * 1. For most use cases, it will repeat requests to remarkable's servers
518
- * (even with a cache) making it slower than implementing similar steps
519
- * manually.
520
- * 2. It includes no handling of concurrent modification or other networking
521
- * errors, requiring repeat network requests if anything fails.
522
- *
523
- * However, with recent changes in the way this library caches results, it
524
- * won't be terribly inefficient to just do multiple retries of create after
525
- * uploading the file itself.
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
526
725
  *
527
- * @example
528
- * ```ts
529
- * const entry = await api.putEpub(...);
530
- * await api.create(entry);
531
- * ```
726
+ * This returns the raw input epub if a document was created from an epub.
532
727
  *
533
- * @param entry - and entry, usually created by a `put*` method
534
- * @param options - any extra options for creation
535
- * @returns synced - if sync was successful
536
- * @throws error - if any error occurred, in this case, nothing will be
537
- * changed
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
538
731
  */
539
- create(entry: CollectionEntry, options?: CreateMoveOptions): Promise<boolean>;
732
+ getEpub(hash: string): Promise<Uint8Array>;
540
733
  /**
541
- * high level api to move a document / collection
734
+ * get the entire contents of a remarkable document
542
735
  *
543
- * Use this as a high level api to move files in the document tree.
736
+ * This gets every file of associated with a document, and puts them into a
737
+ * zip archive.
544
738
  *
545
739
  * @remarks
546
- * This API is provided to give a high level interface and to serve as an
547
- * example of how to implement more advanced functionality, but it comes with
548
- * a number of caveats:
549
- * 1. For most use cases, it will repeat requests to remarkable's servers
550
- * (even with a cache) making it slower than implementing similar steps
551
- * manually.
552
- * 2. It includes no handling of concurrent modification or other networking
553
- * errors, requiring repeat network requests if anything fails.
554
- *
555
- * However, with changes to the way this caches results, it won't be terribly
556
- * inefficient to just retry calling `move` if there's a failure.
557
- *
558
- * @example
559
- * ```ts
560
- * const [root] = await api.getRootHash();
561
- * const entries = await api.getEntries(root);
562
- * const { documentId } = entries.find(...);
563
- * await api.move(documentId, "trash");
564
- * ```
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.
565
743
  *
566
- * @param documentId - the document id of the document or collection to move
567
- * @param dest - the new parent of this collection or document; this should
568
- * be the document id of an existing collection, an empty string for root,
569
- * or the string `"trash"` to move to the trash
570
- * @param opts - any extra options for moving
571
- * @returns synced - true if synced successfully
572
- * @throws error - if any error occurred, in this case, nothing will be
573
- * changed
744
+ * @param hash - the hash of the document to get the contents for (e.g. the
745
+ * hash received from `listItems`)
574
746
  */
575
- move(documentId: string, dest: string, opts?: CreateMoveOptions): Promise<boolean>;
747
+ getDocument(hash: string): Promise<Uint8Array>;
576
748
  /**
577
- * get metadata on all entries
578
- *
579
- * @remarks this uses a newer api, that returns metadata associated with all
580
- * entries and their hash and documentId.
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
581
778
  */
582
- getEntriesMetadata(opts?: GetOptions): Promise<MetadataEntry[]>;
779
+ putEpub(visibleName: string, buffer: Uint8Array, opts?: PutOptions): Promise<SimpleEntry>;
780
+ /** create a folder */
781
+ createFolder(visibleName: string, opts?: UploadOptions): Promise<SimpleEntry>;
583
782
  /**
584
783
  * upload an epub
585
784
  *
586
- * @remarks this uses a newer api that performs a full upload and sync, but
587
- * doesn't allow adding the same level of extra content data. Useful if you
588
- * just want to upload a document with no fuss.
589
- *
590
785
  * @example
591
786
  * ```ts
592
787
  * await api.uploadEpub("My EPub", ...);
@@ -595,17 +790,10 @@ export interface RemarkableApi {
595
790
  * @param visibleName - the name to show for the uploaded epub
596
791
  * @param buffer - the epub contents
597
792
  */
598
- uploadEpub(visibleName: string, buffer: ArrayBuffer, opts?: GetOptions): Promise<UploadEntry>;
793
+ uploadEpub(visibleName: string, buffer: Uint8Array, opts?: UploadOptions): Promise<SimpleEntry>;
599
794
  /**
600
795
  * upload a pdf
601
796
  *
602
- * this currently uploads invalid pdfs, but it's not clear why, which is why
603
- * this is marked as experimental. Potentially some tweak in the formatting
604
- * of buffer will fix it.
605
- *
606
- * @remarks this uses a newer api that performs a full upload and sync, but
607
- * doesn't allow adding the same level of extra information.
608
- *
609
797
  * @example
610
798
  * ```ts
611
799
  * await api.uploadPdf("My PDF", ...);
@@ -613,47 +801,97 @@ export interface RemarkableApi {
613
801
  *
614
802
  * @param visibleName - the name to show for the uploaded epub
615
803
  * @param buffer - the epub contents
616
- * @experimental
617
804
  */
618
- uploadPdf(visibleName: string, buffer: ArrayBuffer, opts?: GetOptions): Promise<UploadEntry>;
805
+ uploadPdf(visibleName: string, buffer: Uint8Array, opts?: UploadOptions): Promise<SimpleEntry>;
619
806
  /**
620
- * get the current state of the cache for persisting
807
+ * move an entry
621
808
  *
622
- * This won't include items that weren't cached because they were too big,
623
- * meaning it won't prevent duplicate puts of large content across sessions.
809
+ * @example
810
+ * ```ts
811
+ * await api.move(doc.hash, dir.id);
812
+ * ```
813
+ *
814
+ * @param hash - the hash of the file to move
815
+ * @param parent - the id of the directory to move the entry to, "" (root) and "trash" are special parents
624
816
  */
625
- getCache(): Promise<Map<string, ArrayBuffer>>;
626
- }
627
- /** format an entry */
628
- export declare function formatEntry({ hash, type, documentId, subfiles, size, }: Entry): string;
629
- /** parse an entry */
630
- export declare function parseEntry(line: string): Entry;
631
- /** options for a remarkable instance */
632
- export interface RemarkableOptions {
817
+ move(hash: string, parent: string): Promise<HashEntry>;
633
818
  /**
634
- * the fetch method to use
819
+ * delete an entry
635
820
  *
636
- * This should loosely conform to the WHATWG fetch, but is relaxed enough that
637
- * node-fetch also works. This will default to the global definitions of
638
- * fetch.
821
+ * @example
822
+ * ```ts
823
+ * await api.delete(file.hash);
824
+ * ```
825
+ * @param hash - the hash of the entry to delete
826
+ */
827
+ delete(hash: string): Promise<HashEntry>;
828
+ /**
829
+ * rename an entry
639
830
  *
640
- * In node you can either use `"node-fetch"`, or `node --experimental-fetch`
641
- * for node 17.5 or higher.
831
+ * @example
832
+ * ```ts
833
+ * await api.rename(file.hash, "new name");
834
+ * ```
835
+ * @param hash - the hash of the entry to rename
836
+ * @param visibleName - the new name to assign
837
+ */
838
+ rename(hash: string, visibleName: string): Promise<HashEntry>;
839
+ /**
840
+ * move many entries
841
+ *
842
+ * @example
843
+ * ```ts
844
+ * await api.bulkMove([file.hash], dir.id);
845
+ * ```
846
+ *
847
+ * @param hashes - an array of entry hashes to move
848
+ * @param parent - the directory id to move the entries to, "" (root) and "trash" are special ids
849
+ */
850
+ bulkMove(hashes: readonly string[], parent: string): Promise<HashesEntry>;
851
+ /**
852
+ * delete many entries
853
+ *
854
+ * @example
855
+ * ```ts
856
+ * await api.bulkDelete([file.hash]);
857
+ * ```
858
+ *
859
+ * @param hashes - the hashes of the entries to delete
860
+ */
861
+ bulkDelete(hashes: readonly string[]): Promise<HashesEntry>;
862
+ /**
863
+ * get the current cache value as a string
642
864
  *
643
- * @defaultValue globalThis.fetch
865
+ * You can use this to warm start a new instance of
866
+ * {@link remarkable | `remarkable`} with any previously cached results.
644
867
  */
645
- fetch?: FetchLike;
868
+ dumpCache(): string;
646
869
  /**
647
- * a subtle-crypto-like object
870
+ * prune the cache so that it contains only reachable hashes
648
871
  *
649
- * This should have a digest function like the api of `crypto.subtle`, it's
650
- * default value. In node try
651
- * `import { webcrypto } from "crypto"; global.crypto = webcrypto` or pass in
652
- * `webcrypto.subtle`.
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.
876
+ *
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.
881
+ *
882
+ * @param refresh - whether to refresh the root hash before pruning
883
+ */
884
+ pruneCache(refresh?: boolean): Promise<void>;
885
+ /**
886
+ * completely delete the cache
653
887
  *
654
- * @defaultValue globalThis.crypto.subtle
888
+ * If the cache is causing memory issues, you can clear it, but this will hurt
889
+ * performance.
655
890
  */
656
- subtle?: SubtleCryptoLike;
891
+ clearCache(): void;
892
+ }
893
+ /** options for a remarkable instance */
894
+ export interface RemarkableOptions {
657
895
  /**
658
896
  * the url for making authorization requests
659
897
  *
@@ -663,30 +901,32 @@ export interface RemarkableOptions {
663
901
  /**
664
902
  * the url for making synchronization requests
665
903
  *
666
- * @defaultValue "https://internal.cloud.remarkable.com"
904
+ * @defaultValue "https://web.eu.tectonic.remarkable.com"
667
905
  */
668
906
  syncHost?: string;
669
907
  /**
670
- * the maximum size in bytes to cache the value of a stored object
671
- *
672
- * Since the remarkableApi is based around hashes, the value of a hash should
673
- * never change (barring collisions in ASH256). Any known hash value that's
674
- * less than this amount will be cached locally to prevent future network
675
- * requests. In addition, all successful puts and gets will be cached to
676
- * prevent duplicate puts in the future.
908
+ * the url for making requests using the low-level api
677
909
  *
678
- * To save memory and disable fetch caching, set to 0.
910
+ * @defaultValue "https://eu.tectonic.remarkable.com"
911
+ */
912
+ rawHost?: string;
913
+ /**
914
+ * an initial cache value
679
915
  *
680
- * @defaultValue 1 MiB
916
+ * Generated from calling {@link RemarkableApi.dumpCache | `dumpCache`} on a previous
917
+ * instance.
681
918
  */
682
- cacheLimitBytes?: number;
919
+ cache?: string;
683
920
  /**
684
- * a set of values to use to initialize the cache
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.
685
926
  *
686
- * If this is inaccurate, then you could encounter errors with other methods.
687
- * Often this will come from {@link RemarkableApi.getCache | `getCache`}.
927
+ * @defaultValue Infinity
688
928
  */
689
- initCache?: Iterable<readonly [string, ArrayBuffer]>;
929
+ maxCacheSize?: number;
690
930
  }
691
931
  /**
692
932
  * create an instance of the api
@@ -698,4 +938,4 @@ export interface RemarkableOptions {
698
938
  * registered. Create one with {@link register}.
699
939
  * @returns an api instance
700
940
  */
701
- export declare function remarkable(deviceToken: string, { fetch, subtle, authHost, syncHost, cacheLimitBytes, initCache, }?: RemarkableOptions): Promise<RemarkableApi>;
941
+ export declare function remarkable(deviceToken: string, { authHost, syncHost, rawHost, cache, maxCacheSize, }?: RemarkableOptions): Promise<RemarkableApi>;