rmapi-js 8.2.0 → 8.4.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 +21 -20
- package/dist/index.js +20 -14
- package/dist/raw.d.ts +54 -9
- package/dist/raw.js +30 -4
- package/dist/rmapi-js.esm.min.js +10 -10
- package/package.json +11 -11
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type BackgroundFilter, type CollectionContent, type Content, type DocumentContent, type Metadata, type Orientation, type RawRemarkableApi, type Tag, type TemplateContent, type TextAlignment, type ZoomMode } from "./raw";
|
|
1
|
+
import { type BackgroundFilter, type CollectionContent, type Content, type DocumentContent, type Metadata, type Orientation, type RawRemarkableApi, type SimpleEntry, type Tag, type TemplateContent, type TextAlignment, type ZoomMode } from "./raw";
|
|
2
2
|
export { HashNotFoundError, ValidationError } from "./error";
|
|
3
|
-
export type { BackgroundFilter, CollectionContent, Content, CPageNumberValue, CPagePage, CPages, CPageStringValue, CPageUUID, DocumentContent, DocumentMetadata, FileType, KeyboardMetadata, Metadata, Orientation, PageTag, RawEntry, RawFileEntry, RawListEntry, RawRemarkableApi, Tag, TemplateContent, TextAlignment, ZoomMode, } from "./raw";
|
|
3
|
+
export type { BackgroundFilter, CollectionContent, Content, CPageNumberValue, CPagePage, CPages, CPageStringValue, CPageUUID, DocumentContent, DocumentMetadata, FileType, KeyboardMetadata, Metadata, Orientation, PageTag, RawEntry, RawFileEntry, RawListEntry, RawRemarkableApi, SimpleEntry, Tag, TemplateContent, TextAlignment, UploadMimeType, ZoomMode, } from "./raw";
|
|
4
4
|
/** common properties shared by collections and documents */
|
|
5
5
|
export interface EntryCommon {
|
|
6
6
|
/** the document id, a uuid4 */
|
|
@@ -50,13 +50,6 @@ export interface TemplateType extends EntryCommon {
|
|
|
50
50
|
}
|
|
51
51
|
/** a remarkable entry for cloud items */
|
|
52
52
|
export type Entry = CollectionEntry | DocumentType | TemplateType;
|
|
53
|
-
/** an simple entry without any extra information */
|
|
54
|
-
export interface SimpleEntry {
|
|
55
|
-
/** the document id */
|
|
56
|
-
id: string;
|
|
57
|
-
/** the document hash */
|
|
58
|
-
hash: string;
|
|
59
|
-
}
|
|
60
53
|
/** the new hash of a modified entry */
|
|
61
54
|
export interface HashEntry {
|
|
62
55
|
/** the actual hash */
|
|
@@ -67,6 +60,11 @@ export interface HashesEntry {
|
|
|
67
60
|
/** the mapping from old to new hashes */
|
|
68
61
|
hashes: Record<string, string>;
|
|
69
62
|
}
|
|
63
|
+
/** options for creating a folder */
|
|
64
|
+
export interface FolderOptions {
|
|
65
|
+
/** the id of the folder's parent directory, "" or omitted for root */
|
|
66
|
+
parent?: string;
|
|
67
|
+
}
|
|
70
68
|
/** An error that gets thrown when the backend while trying to update
|
|
71
69
|
*
|
|
72
70
|
* IF you encounter this error, you likely just need to try th request again. If
|
|
@@ -114,11 +112,6 @@ export interface RegisterOptions {
|
|
|
114
112
|
* @returns the device token necessary for creating an api instace. These never expire so persist as long as necessary.
|
|
115
113
|
*/
|
|
116
114
|
export declare function register(code: string, { deviceDesc, uuid, authHost, }?: RegisterOptions): Promise<string>;
|
|
117
|
-
/** options available when uploading a document */
|
|
118
|
-
export interface UploadOptions {
|
|
119
|
-
/** an optional parent id to set when uploading */
|
|
120
|
-
parent?: string;
|
|
121
|
-
}
|
|
122
115
|
/**
|
|
123
116
|
* options for putting a file onto reMarkable
|
|
124
117
|
*
|
|
@@ -322,7 +315,7 @@ export interface RemarkableApi {
|
|
|
322
315
|
*/
|
|
323
316
|
putEpub(visibleName: string, buffer: Uint8Array, opts?: PutOptions): Promise<SimpleEntry>;
|
|
324
317
|
/** create a folder */
|
|
325
|
-
|
|
318
|
+
putFolder(visibleName: string, opts?: FolderOptions, refresh?: boolean): Promise<SimpleEntry>;
|
|
326
319
|
/**
|
|
327
320
|
* upload an epub
|
|
328
321
|
*
|
|
@@ -332,12 +325,12 @@ export interface RemarkableApi {
|
|
|
332
325
|
* ```
|
|
333
326
|
*
|
|
334
327
|
* @remarks
|
|
335
|
-
* this
|
|
328
|
+
* this uses a simpler api that works even with schema version 4.
|
|
336
329
|
*
|
|
337
330
|
* @param visibleName - the name to show for the uploaded epub
|
|
338
331
|
* @param buffer - the epub contents
|
|
339
332
|
*/
|
|
340
|
-
uploadEpub(visibleName: string, buffer: Uint8Array
|
|
333
|
+
uploadEpub(visibleName: string, buffer: Uint8Array): Promise<SimpleEntry>;
|
|
341
334
|
/**
|
|
342
335
|
* upload a pdf
|
|
343
336
|
*
|
|
@@ -347,12 +340,14 @@ export interface RemarkableApi {
|
|
|
347
340
|
* ```
|
|
348
341
|
*
|
|
349
342
|
* @remarks
|
|
350
|
-
* this
|
|
343
|
+
* this uses a simpler api that works even with schema version 4.
|
|
351
344
|
*
|
|
352
345
|
* @param visibleName - the name to show for the uploaded epub
|
|
353
346
|
* @param buffer - the epub contents
|
|
354
347
|
*/
|
|
355
|
-
uploadPdf(visibleName: string, buffer: Uint8Array
|
|
348
|
+
uploadPdf(visibleName: string, buffer: Uint8Array): Promise<SimpleEntry>;
|
|
349
|
+
/** create a folder using the simple api */
|
|
350
|
+
uploadFolder(visibleName: string): Promise<SimpleEntry>;
|
|
356
351
|
/**
|
|
357
352
|
* update content metadata for a document
|
|
358
353
|
*
|
|
@@ -501,6 +496,12 @@ export interface RemarkableOptions {
|
|
|
501
496
|
* @defaultValue "https://web.eu.tectonic.remarkable.com"
|
|
502
497
|
*/
|
|
503
498
|
syncHost?: string;
|
|
499
|
+
/**
|
|
500
|
+
* the base url for making upload requests
|
|
501
|
+
*
|
|
502
|
+
* @defaultValue "https://internal.cloud.remarkable.com"
|
|
503
|
+
*/
|
|
504
|
+
uploadHost?: string;
|
|
504
505
|
/**
|
|
505
506
|
* the url for making requests using the low-level api
|
|
506
507
|
*
|
|
@@ -535,4 +536,4 @@ export interface RemarkableOptions {
|
|
|
535
536
|
* registered. Create one with {@link register}.
|
|
536
537
|
* @returns an api instance
|
|
537
538
|
*/
|
|
538
|
-
export declare function remarkable(deviceToken: string, { authHost, rawHost, cache, maxCacheSize, }?: RemarkableOptions): Promise<RemarkableApi>;
|
|
539
|
+
export declare function remarkable(deviceToken: string, { authHost, rawHost, uploadHost, cache, maxCacheSize, }?: RemarkableOptions): Promise<RemarkableApi>;
|
package/dist/index.js
CHANGED
|
@@ -60,6 +60,7 @@ import { RawRemarkable, } from "./raw";
|
|
|
60
60
|
export { HashNotFoundError, ValidationError } from "./error";
|
|
61
61
|
const AUTH_HOST = "https://webapp-prod.cloud.remarkable.engineering";
|
|
62
62
|
const RAW_HOST = "https://eu.tectonic.remarkable.com";
|
|
63
|
+
const UPLOAD_HOST = "https://internal.cloud.remarkable.com";
|
|
63
64
|
// ------------ //
|
|
64
65
|
// Request Info //
|
|
65
66
|
// ------------ //
|
|
@@ -130,10 +131,10 @@ class Remarkable {
|
|
|
130
131
|
#cache;
|
|
131
132
|
raw;
|
|
132
133
|
#lastHashGen;
|
|
133
|
-
constructor(userToken, rawHost, cache) {
|
|
134
|
+
constructor(userToken, rawHost, uploadHost, cache) {
|
|
134
135
|
this.#userToken = userToken;
|
|
135
136
|
this.#cache = cache;
|
|
136
|
-
this.raw = new RawRemarkable((method, url, { body, headers } = {}) => this.#authedFetch(url, { method, body, headers }), cache, rawHost);
|
|
137
|
+
this.raw = new RawRemarkable((method, url, { body, headers } = {}) => this.#authedFetch(url, { method, body, headers }), cache, rawHost, uploadHost);
|
|
137
138
|
}
|
|
138
139
|
async #getRootHash(refresh = false) {
|
|
139
140
|
if (refresh || this.#lastHashGen === undefined) {
|
|
@@ -160,7 +161,8 @@ class Remarkable {
|
|
|
160
161
|
Authorization: `Bearer ${this.#userToken}`,
|
|
161
162
|
...headers,
|
|
162
163
|
},
|
|
163
|
-
|
|
164
|
+
// fetch works correctly with uint8 arrays, but is not hinted correctly
|
|
165
|
+
body: body,
|
|
164
166
|
});
|
|
165
167
|
if (!resp.ok) {
|
|
166
168
|
const msg = await resp.text();
|
|
@@ -182,12 +184,12 @@ class Remarkable {
|
|
|
182
184
|
if (metaEnt === undefined) {
|
|
183
185
|
throw new Error(`couldn't find metadata for hash ${hash}`);
|
|
184
186
|
}
|
|
185
|
-
else if (contentEnt === undefined) {
|
|
186
|
-
throw new Error(`couldn't find content for hash ${hash}`);
|
|
187
|
-
}
|
|
188
187
|
const [{ visibleName, lastModified, pinned, parent, lastOpened, new: isNew, source, }, content,] = await Promise.all([
|
|
189
188
|
this.raw.getMetadata(metaEnt.hash),
|
|
190
|
-
|
|
189
|
+
// collections don't always have content, since content only lists tags
|
|
190
|
+
contentEnt === undefined
|
|
191
|
+
? Promise.resolve({ fileType: undefined, tags: undefined })
|
|
192
|
+
: this.raw.getContent(contentEnt.hash),
|
|
191
193
|
]);
|
|
192
194
|
if ("templateVersion" in content) {
|
|
193
195
|
return {
|
|
@@ -373,7 +375,7 @@ class Remarkable {
|
|
|
373
375
|
return await this.#putFile(visibleName, "epub", buffer, opts);
|
|
374
376
|
}
|
|
375
377
|
/** create a folder */
|
|
376
|
-
async
|
|
378
|
+
async putFolder(visibleName, { parent = "" } = {}, refresh = false) {
|
|
377
379
|
if (parent && !idReg.test(parent)) {
|
|
378
380
|
throw new ValidationError(parent, idReg, "parent must be a valid document id");
|
|
379
381
|
}
|
|
@@ -416,12 +418,16 @@ class Remarkable {
|
|
|
416
418
|
return { id, hash: collectionEntry.hash };
|
|
417
419
|
}
|
|
418
420
|
/** upload an epub */
|
|
419
|
-
async uploadEpub(visibleName, buffer
|
|
420
|
-
return await this.
|
|
421
|
+
async uploadEpub(visibleName, buffer) {
|
|
422
|
+
return await this.raw.uploadFile(visibleName, buffer, "application/epub+zip");
|
|
421
423
|
}
|
|
422
424
|
/** upload a pdf */
|
|
423
|
-
async uploadPdf(visibleName, buffer
|
|
424
|
-
return await this.
|
|
425
|
+
async uploadPdf(visibleName, buffer) {
|
|
426
|
+
return await this.raw.uploadFile(visibleName, buffer, "application/pdf");
|
|
427
|
+
}
|
|
428
|
+
/** upload a folder */
|
|
429
|
+
async uploadFolder(visibleName) {
|
|
430
|
+
return await this.raw.uploadFile(visibleName, new Uint8Array(0), "folder");
|
|
425
431
|
}
|
|
426
432
|
/** edit just a content entry */
|
|
427
433
|
async #editContentRaw(id, hash, update) {
|
|
@@ -599,7 +605,7 @@ const cached = values(nullable(string()));
|
|
|
599
605
|
* registered. Create one with {@link register}.
|
|
600
606
|
* @returns an api instance
|
|
601
607
|
*/
|
|
602
|
-
export async function remarkable(deviceToken, { authHost = AUTH_HOST, rawHost = RAW_HOST, cache, maxCacheSize = Infinity, } = {}) {
|
|
608
|
+
export async function remarkable(deviceToken, { authHost = AUTH_HOST, rawHost = RAW_HOST, uploadHost = UPLOAD_HOST, cache, maxCacheSize = Infinity, } = {}) {
|
|
603
609
|
const resp = await fetch(`${authHost}/token/json/2/user/new`, {
|
|
604
610
|
method: "POST",
|
|
605
611
|
headers: {
|
|
@@ -616,7 +622,7 @@ export async function remarkable(deviceToken, { authHost = AUTH_HOST, rawHost =
|
|
|
616
622
|
const cache = maxCacheSize === Infinity
|
|
617
623
|
? new Map(entries)
|
|
618
624
|
: new LruCache(maxCacheSize, entries);
|
|
619
|
-
return new Remarkable(userToken, rawHost, cache);
|
|
625
|
+
return new Remarkable(userToken, rawHost, uploadHost, cache);
|
|
620
626
|
}
|
|
621
627
|
else {
|
|
622
628
|
throw new Error("cache was not a valid cache (json string mapping); your cache must be corrupted somehow. Either initialize remarkable without a cache, or fix its format.");
|
package/dist/raw.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
/** request types */
|
|
2
2
|
export type RequestMethod = "POST" | "GET" | "PUT" | "DELETE" | "PATCH" | "OPTIONS";
|
|
3
|
+
/** the supported upload mime types */
|
|
4
|
+
export type UploadMimeType = "application/pdf" | "application/epub+zip" | "folder";
|
|
5
|
+
/** an simple entry without any extra information */
|
|
6
|
+
export interface SimpleEntry {
|
|
7
|
+
/** the document id */
|
|
8
|
+
id: string;
|
|
9
|
+
/** the document hash */
|
|
10
|
+
hash: string;
|
|
11
|
+
}
|
|
3
12
|
/**
|
|
4
13
|
* the low-level entry corresponding to a collection of files
|
|
5
14
|
*
|
|
@@ -167,7 +176,7 @@ export interface DocumentContent {
|
|
|
167
176
|
*/
|
|
168
177
|
fontName: string;
|
|
169
178
|
/** the format version, this should always be 1 */
|
|
170
|
-
formatVersion
|
|
179
|
+
formatVersion?: number;
|
|
171
180
|
/** the last opened page, starts at zero */
|
|
172
181
|
lastOpenedPage?: number;
|
|
173
182
|
/**
|
|
@@ -210,17 +219,37 @@ export interface DocumentContent {
|
|
|
210
219
|
* values outside of this range are valid.
|
|
211
220
|
*/
|
|
212
221
|
textScale: number;
|
|
213
|
-
/**
|
|
222
|
+
/**
|
|
223
|
+
* the center of the zoom for customFit zoom
|
|
224
|
+
*
|
|
225
|
+
* This is an absolute offset from the center of the page. Negative numbers
|
|
226
|
+
* indicate shifted left and positive numbers indicate shifted right. The
|
|
227
|
+
* units are relative to the document pixels, but it's not sure how the
|
|
228
|
+
* document size is calculated.
|
|
229
|
+
*/
|
|
214
230
|
customZoomCenterX?: number;
|
|
215
|
-
/**
|
|
231
|
+
/**
|
|
232
|
+
* the center of the zoom for customFit documents
|
|
233
|
+
*
|
|
234
|
+
* This is an absolute number relative to the top of the page. Negative
|
|
235
|
+
* numbers indicate shifted up, while positive numbers indicate shifted down.
|
|
236
|
+
* The units are relative to the document pixels, but it's not sure how the
|
|
237
|
+
* document size is calculated.
|
|
238
|
+
*/
|
|
216
239
|
customZoomCenterY?: number;
|
|
217
|
-
/**
|
|
240
|
+
/** this seems unused */
|
|
218
241
|
customZoomOrientation?: Orientation;
|
|
219
|
-
/**
|
|
242
|
+
/** this seems unused */
|
|
220
243
|
customZoomPageHeight?: number;
|
|
221
|
-
/**
|
|
244
|
+
/** this seems unused */
|
|
222
245
|
customZoomPageWidth?: number;
|
|
223
|
-
/**
|
|
246
|
+
/**
|
|
247
|
+
* the scale for customFit documents
|
|
248
|
+
*
|
|
249
|
+
* 1 indicates no zoom, smaller numbers indicate zoomed out, larger numbers
|
|
250
|
+
* indicate zoomed in. reMarkable generally allows setting this from 0.5 to 5,
|
|
251
|
+
* but values outside that bound are still supported.
|
|
252
|
+
*/
|
|
224
253
|
customZoomScale?: number;
|
|
225
254
|
/** what zoom mode is set for the page */
|
|
226
255
|
zoomMode?: ZoomMode;
|
|
@@ -261,7 +290,7 @@ export interface TemplateContent {
|
|
|
261
290
|
/** semantic version for this template */
|
|
262
291
|
templateVersion: string;
|
|
263
292
|
/** template configuration format version (currently just `1`) */
|
|
264
|
-
formatVersion
|
|
293
|
+
formatVersion?: number;
|
|
265
294
|
/**
|
|
266
295
|
* which screens the template supports:
|
|
267
296
|
*
|
|
@@ -498,9 +527,24 @@ export interface RawRemarkableApi {
|
|
|
498
527
|
* @param id - the id of the list to upload - this should be the item id if
|
|
499
528
|
* uploading an item list, or "root" if uploading a new root list.
|
|
500
529
|
* @param entries - the entries to upload
|
|
530
|
+
*
|
|
501
531
|
* @returns the new list entry and a promise to finish the upload
|
|
502
532
|
*/
|
|
503
533
|
putEntries(id: string, entries: RawEntry[]): Promise<[RawListEntry, Promise<void>]>;
|
|
534
|
+
/**
|
|
535
|
+
* upload a file to the reMarkable cloud using the simple api
|
|
536
|
+
*
|
|
537
|
+
* This api is the same as used by the native reMarkable extension and works
|
|
538
|
+
* even if the backend schema version is version 4. Setting mime to "folder"
|
|
539
|
+
* allows folder creation.
|
|
540
|
+
*
|
|
541
|
+
* @param visibleName - the name of the file as it should appear on the reMarkable
|
|
542
|
+
* @param bytes - the bytes of the file to upload
|
|
543
|
+
* @param mime - the mime type of the file to upload
|
|
544
|
+
|
|
545
|
+
* @returns a simple entry with the id and hash of the uploaded file
|
|
546
|
+
*/
|
|
547
|
+
uploadFile(visibleName: string, bytes: Uint8Array, mime: UploadMimeType): Promise<SimpleEntry>;
|
|
504
548
|
/**
|
|
505
549
|
* dump the current cache to a string to preserve between session
|
|
506
550
|
*
|
|
@@ -518,7 +562,7 @@ interface AuthedFetch {
|
|
|
518
562
|
}
|
|
519
563
|
export declare class RawRemarkable implements RawRemarkableApi {
|
|
520
564
|
#private;
|
|
521
|
-
constructor(authedFetch: AuthedFetch, cache: Map<string, string | null>, rawHost: string);
|
|
565
|
+
constructor(authedFetch: AuthedFetch, cache: Map<string, string | null>, rawHost: string, uploadHost: string);
|
|
522
566
|
/** make an authorized request to remarkable */
|
|
523
567
|
getRootHash(): Promise<[string, number]>;
|
|
524
568
|
getHash(hash: string): Promise<Uint8Array>;
|
|
@@ -532,6 +576,7 @@ export declare class RawRemarkable implements RawRemarkableApi {
|
|
|
532
576
|
putContent(id: string, content: Content): Promise<[RawFileEntry, Promise<void>]>;
|
|
533
577
|
putMetadata(id: string, metadata: Metadata): Promise<[RawFileEntry, Promise<void>]>;
|
|
534
578
|
putEntries(id: string, entries: RawEntry[]): Promise<[RawListEntry, Promise<void>]>;
|
|
579
|
+
uploadFile(visibleName: string, bytes: Uint8Array, mime: UploadMimeType): Promise<SimpleEntry>;
|
|
535
580
|
dumpCache(): string;
|
|
536
581
|
clearCache(): void;
|
|
537
582
|
}
|
package/dist/raw.js
CHANGED
|
@@ -70,7 +70,6 @@ const documentContent = properties({
|
|
|
70
70
|
extraMetadata: values(string()),
|
|
71
71
|
fileType: enumeration("epub", "notebook", "pdf"),
|
|
72
72
|
fontName: string(),
|
|
73
|
-
formatVersion: uint8(),
|
|
74
73
|
lineHeight: int32(),
|
|
75
74
|
orientation: enumeration("portrait", "landscape"),
|
|
76
75
|
pageCount: uint32(),
|
|
@@ -86,6 +85,7 @@ const documentContent = properties({
|
|
|
86
85
|
customZoomPageWidth: float64(),
|
|
87
86
|
customZoomScale: float64(),
|
|
88
87
|
dummyDocument: boolean(),
|
|
88
|
+
formatVersion: uint8(),
|
|
89
89
|
keyboardMetadata: properties({
|
|
90
90
|
count: uint32(),
|
|
91
91
|
timestamp: float64(),
|
|
@@ -120,10 +120,11 @@ const templateContent = properties({
|
|
|
120
120
|
labels: elements(string()),
|
|
121
121
|
orientation: enumeration("portrait", "landscape"),
|
|
122
122
|
templateVersion: string(),
|
|
123
|
-
formatVersion: uint8(),
|
|
124
123
|
supportedScreens: elements(enumeration("rm2", "rmPP")),
|
|
125
124
|
constants: elements(values(int32())),
|
|
126
125
|
items: elements(empty()),
|
|
126
|
+
}, {
|
|
127
|
+
formatVersion: uint8(),
|
|
127
128
|
});
|
|
128
129
|
const metadata = properties({
|
|
129
130
|
lastModified: string(),
|
|
@@ -150,8 +151,14 @@ const rootHash = properties({
|
|
|
150
151
|
generation: float64(),
|
|
151
152
|
schemaVersion: uint8(),
|
|
152
153
|
}, undefined, true);
|
|
154
|
+
const NativeSimpleEntry = properties({
|
|
155
|
+
docID: string(),
|
|
156
|
+
hash: string(),
|
|
157
|
+
}, undefined, true);
|
|
153
158
|
async function digest(buff) {
|
|
154
|
-
const digest = await crypto.subtle.digest("SHA-256",
|
|
159
|
+
const digest = await crypto.subtle.digest("SHA-256",
|
|
160
|
+
// NOTE this is type hinted wrong, but it does work correctly on a uint8 view
|
|
161
|
+
buff);
|
|
155
162
|
return [...new Uint8Array(digest)]
|
|
156
163
|
.map((x) => x.toString(16).padStart(2, "0"))
|
|
157
164
|
.join("");
|
|
@@ -159,6 +166,7 @@ async function digest(buff) {
|
|
|
159
166
|
export class RawRemarkable {
|
|
160
167
|
#authedFetch;
|
|
161
168
|
#rawHost;
|
|
169
|
+
#uploadHost;
|
|
162
170
|
/**
|
|
163
171
|
* a cache of all hashes we know exist
|
|
164
172
|
*
|
|
@@ -169,10 +177,11 @@ export class RawRemarkable {
|
|
|
169
177
|
* not to write a a cached value again, but we'll still need to read it.
|
|
170
178
|
*/
|
|
171
179
|
#cache;
|
|
172
|
-
constructor(authedFetch, cache, rawHost) {
|
|
180
|
+
constructor(authedFetch, cache, rawHost, uploadHost) {
|
|
173
181
|
this.#authedFetch = authedFetch;
|
|
174
182
|
this.#cache = cache;
|
|
175
183
|
this.#rawHost = rawHost;
|
|
184
|
+
this.#uploadHost = uploadHost;
|
|
176
185
|
}
|
|
177
186
|
/** make an authorized request to remarkable */
|
|
178
187
|
async getRootHash() {
|
|
@@ -417,6 +426,23 @@ export class RawRemarkable {
|
|
|
417
426
|
this.#putFile(hash, `${id}.docSchema`, enc.encode(records.join(""))),
|
|
418
427
|
];
|
|
419
428
|
}
|
|
429
|
+
async uploadFile(visibleName, bytes, mime) {
|
|
430
|
+
const enc = new TextEncoder();
|
|
431
|
+
const meta = fromByteArray(enc.encode(JSON.stringify({ file_name: visibleName })));
|
|
432
|
+
const resp = await this.#authedFetch("POST", `${this.#uploadHost}/doc/v2/files`, {
|
|
433
|
+
body: bytes,
|
|
434
|
+
headers: {
|
|
435
|
+
"Content-Type": mime,
|
|
436
|
+
"rm-meta": meta,
|
|
437
|
+
"rm-source": "RoR-Browser",
|
|
438
|
+
},
|
|
439
|
+
});
|
|
440
|
+
const loaded = (await resp.json());
|
|
441
|
+
if (!NativeSimpleEntry.guardAssert(loaded))
|
|
442
|
+
throw Error("invalid upload response");
|
|
443
|
+
const { docID, hash } = loaded;
|
|
444
|
+
return { id: docID, hash };
|
|
445
|
+
}
|
|
420
446
|
dumpCache() {
|
|
421
447
|
return JSON.stringify(Object.fromEntries(this.#cache));
|
|
422
448
|
}
|