rmapi-js 10.1.0 → 10.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/index.d.ts +3 -9
- package/dist/index.js +25 -22
- package/dist/raw.d.ts +3 -3
- package/dist/raw.js +206 -197
- package/dist/rmapi-js.esm.min.js +10 -23
- package/package.json +4 -5
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ const fileEntries = await api.listItems();
|
|
|
35
35
|
|
|
36
36
|
// In stateless environments, exchange once and reuse.
|
|
37
37
|
const sessionToken = await auth(token);
|
|
38
|
-
const
|
|
38
|
+
const sessionApi = session(sessionToken);
|
|
39
39
|
// cache `sessionToken` and reuse it across workers
|
|
40
40
|
```
|
|
41
41
|
|
|
@@ -62,8 +62,8 @@ Using these apis is a little riskier since they can potentially result in data l
|
|
|
62
62
|
// upload with custom line height not avilable through reMarkable
|
|
63
63
|
await api.putEpub("name", buffer, { lineHeight: 180 })
|
|
64
64
|
|
|
65
|
-
// fetch an uploaded
|
|
66
|
-
const buffer = await api.getEpub(hash)
|
|
65
|
+
// fetch an uploaded epub, using the id and hash (from listItems)
|
|
66
|
+
const buffer = await api.getEpub(id, hash)
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
### Gotchas
|
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 SimpleEntry, type Tag, type TemplateContent, type TextAlignment, type ZoomMode } from "./raw";
|
|
2
|
-
export { HashNotFoundError, ValidationError } from "./error";
|
|
3
|
-
export type { BackgroundFilter, CollectionContent, Content, CPageNumberValue, CPagePage, CPageStringValue, CPages, CPageUUID, DocumentContent, DocumentMetadata, Entries, FileType, KeyboardMetadata, LegacyCollectionContent, LegacyDocumentContent, Metadata, Orientation, PageTag, RawEntry, RawRemarkableApi, SchemaVersion, SimpleEntry, Tag, TemplateContent, TextAlignment, UploadMimeType, 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.js";
|
|
2
|
+
export { HashNotFoundError, ValidationError } from "./error.js";
|
|
3
|
+
export type { BackgroundFilter, CollectionContent, Content, CPageNumberValue, CPagePage, CPageStringValue, CPages, CPageUUID, DocumentContent, DocumentMetadata, Entries, FileType, KeyboardMetadata, LegacyCollectionContent, LegacyDocumentContent, Metadata, Orientation, PageTag, RawEntry, RawRemarkableApi, SchemaVersion, SimpleEntry, Tag, TemplateContent, TextAlignment, UploadMimeType, ZoomMode, } from "./raw.js";
|
|
4
4
|
/** common properties shared by collections and documents */
|
|
5
5
|
export interface EntryCommon {
|
|
6
6
|
/** the document id, a uuid4 */
|
|
@@ -498,12 +498,6 @@ export interface AuthOptions {
|
|
|
498
498
|
}
|
|
499
499
|
/** options for constructing an api instance from a session token */
|
|
500
500
|
export interface RemarkableSessionOptions {
|
|
501
|
-
/**
|
|
502
|
-
* the url for making synchronization requests
|
|
503
|
-
*
|
|
504
|
-
* @defaultValue "https://web.eu.tectonic.remarkable.com"
|
|
505
|
-
*/
|
|
506
|
-
syncHost?: string;
|
|
507
501
|
/**
|
|
508
502
|
* the base url for making upload requests
|
|
509
503
|
*
|
package/dist/index.js
CHANGED
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
* // persist token
|
|
18
18
|
* const api = await remarkable(token);
|
|
19
19
|
* // list all items (documents and collections)
|
|
20
|
-
* const [first, ...rest] = api.listItems();
|
|
20
|
+
* const [first, ...rest] = await api.listItems();
|
|
21
21
|
* // rename first item
|
|
22
|
-
* const entry = api.rename(first.hash, "new name");
|
|
22
|
+
* const entry = await api.rename(first.hash, "new name");
|
|
23
23
|
* ```
|
|
24
24
|
*
|
|
25
25
|
* @example
|
|
@@ -52,12 +52,12 @@
|
|
|
52
52
|
* @packageDocumentation
|
|
53
53
|
*/
|
|
54
54
|
import JSZip from "jszip";
|
|
55
|
-
import { nullable, string, values } from "jtd-ts";
|
|
56
55
|
import { v4 as uuid4 } from "uuid";
|
|
57
|
-
import {
|
|
58
|
-
import {
|
|
59
|
-
import {
|
|
60
|
-
|
|
56
|
+
import { z } from "zod";
|
|
57
|
+
import { HashNotFoundError, ValidationError } from "./error.js";
|
|
58
|
+
import { LruCache } from "./lru.js";
|
|
59
|
+
import { RawRemarkable, } from "./raw.js";
|
|
60
|
+
export { HashNotFoundError, ValidationError } from "./error.js";
|
|
61
61
|
const AUTH_HOST = "https://webapp-prod.cloud.remarkable.engineering";
|
|
62
62
|
const RAW_HOST = "https://eu.tectonic.remarkable.com";
|
|
63
63
|
const UPLOAD_HOST = "https://internal.cloud.remarkable.com";
|
|
@@ -131,6 +131,7 @@ class Remarkable {
|
|
|
131
131
|
#cache;
|
|
132
132
|
raw;
|
|
133
133
|
#lastHashGen;
|
|
134
|
+
#schemaVersion;
|
|
134
135
|
constructor(sessionToken, rawHost, uploadHost, cache) {
|
|
135
136
|
this.#sessionToken = sessionToken;
|
|
136
137
|
this.#cache = cache;
|
|
@@ -138,15 +139,16 @@ class Remarkable {
|
|
|
138
139
|
}
|
|
139
140
|
async #getRootHash(refresh = false) {
|
|
140
141
|
if (refresh || this.#lastHashGen === undefined) {
|
|
141
|
-
|
|
142
|
+
const [hash, generation, schemaVersion] = await this.raw.getRootHash();
|
|
143
|
+
this.#lastHashGen = [hash, generation];
|
|
144
|
+
this.#schemaVersion = schemaVersion;
|
|
142
145
|
}
|
|
143
|
-
return this.#lastHashGen;
|
|
146
|
+
return [...this.#lastHashGen, this.#schemaVersion];
|
|
144
147
|
}
|
|
145
148
|
async #putRootHash(hash, generation) {
|
|
146
149
|
try {
|
|
147
150
|
const [rootHash, gen] = await this.raw.putRootHash(hash, generation);
|
|
148
|
-
|
|
149
|
-
this.#lastHashGen = [rootHash, gen, schemaVersion];
|
|
151
|
+
this.#lastHashGen = [rootHash, gen];
|
|
150
152
|
}
|
|
151
153
|
catch (ex) {
|
|
152
154
|
// if we hit a generation error, invalidate our cached generation
|
|
@@ -186,7 +188,7 @@ class Remarkable {
|
|
|
186
188
|
if (metaEnt === undefined) {
|
|
187
189
|
throw new Error(`couldn't find metadata for hash ${hash}`);
|
|
188
190
|
}
|
|
189
|
-
const [{ visibleName, lastModified, pinned, parent, lastOpened, new: isNew, source, }, content,] = await Promise.all([
|
|
191
|
+
const [{ visibleName, lastModified, pinned, parent, lastOpened, createdTime, new: isNew, source, }, content,] = await Promise.all([
|
|
190
192
|
this.raw.getMetadata(metaEnt.id, metaEnt.hash),
|
|
191
193
|
// collections don't always have content, since content only lists tags
|
|
192
194
|
contentEnt === undefined
|
|
@@ -203,6 +205,7 @@ class Remarkable {
|
|
|
203
205
|
pinned,
|
|
204
206
|
source,
|
|
205
207
|
parent,
|
|
208
|
+
createdTime,
|
|
206
209
|
type: "TemplateType",
|
|
207
210
|
};
|
|
208
211
|
}
|
|
@@ -562,8 +565,9 @@ class Remarkable {
|
|
|
562
565
|
}
|
|
563
566
|
async pruneCache(refresh) {
|
|
564
567
|
const [rootHash] = await this.#getRootHash(refresh);
|
|
565
|
-
//
|
|
568
|
+
// start by assuming every cached hash is unreachable, then keep the ones we reach
|
|
566
569
|
const toDelete = new Set(this.#cache.keys());
|
|
570
|
+
toDelete.delete(rootHash);
|
|
567
571
|
// bfs through entries (to semi-optimize promise waiting, although this
|
|
568
572
|
// should only go one step) to track all hashes encountered
|
|
569
573
|
// NOTE that we could increase the cache in this process, or it's possible
|
|
@@ -573,9 +577,9 @@ class Remarkable {
|
|
|
573
577
|
let nextEntries = [];
|
|
574
578
|
while (entries.length) {
|
|
575
579
|
for (const entryList of entries) {
|
|
576
|
-
for (const { hash,
|
|
577
|
-
toDelete.
|
|
578
|
-
if (
|
|
580
|
+
for (const { hash, subfiles, id } of entryList) {
|
|
581
|
+
toDelete.delete(hash);
|
|
582
|
+
if (subfiles > 0) {
|
|
579
583
|
nextEntries.push(this.raw.getEntries(`${id}.docSchema`, hash));
|
|
580
584
|
}
|
|
581
585
|
}
|
|
@@ -588,12 +592,11 @@ class Remarkable {
|
|
|
588
592
|
this.#cache.delete(key);
|
|
589
593
|
}
|
|
590
594
|
}
|
|
591
|
-
// finally remove any values we had in the cache initially, but couldn't reach
|
|
592
595
|
clearCache() {
|
|
593
596
|
this.raw.clearCache();
|
|
594
597
|
}
|
|
595
598
|
}
|
|
596
|
-
const cached =
|
|
599
|
+
const cached = z.record(z.string(), z.string().nullable());
|
|
597
600
|
/**
|
|
598
601
|
* Exchange a device token for a session token.
|
|
599
602
|
*
|
|
@@ -624,8 +627,9 @@ export async function auth(deviceToken, { authHost = AUTH_HOST } = {}) {
|
|
|
624
627
|
*/
|
|
625
628
|
export function session(sessionToken, { rawHost = RAW_HOST, uploadHost = UPLOAD_HOST, cache, maxCacheSize = Infinity, } = {}) {
|
|
626
629
|
const initCache = JSON.parse(cache ?? "{}");
|
|
627
|
-
|
|
628
|
-
|
|
630
|
+
const parsedCache = cached.safeParse(initCache);
|
|
631
|
+
if (parsedCache.success) {
|
|
632
|
+
const entries = Object.entries(parsedCache.data);
|
|
629
633
|
const cacheMap = maxCacheSize === Infinity
|
|
630
634
|
? new Map(entries)
|
|
631
635
|
: new LruCache(maxCacheSize, entries);
|
|
@@ -644,13 +648,12 @@ export function session(sessionToken, { rawHost = RAW_HOST, uploadHost = UPLOAD_
|
|
|
644
648
|
* @returns an api instance
|
|
645
649
|
*/
|
|
646
650
|
export async function remarkable(deviceToken, options = {}) {
|
|
647
|
-
const { authHost, rawHost, uploadHost, cache, maxCacheSize
|
|
651
|
+
const { authHost, rawHost, uploadHost, cache, maxCacheSize } = options ?? {};
|
|
648
652
|
const sessionToken = await auth(deviceToken, { authHost });
|
|
649
653
|
return session(sessionToken, {
|
|
650
654
|
rawHost,
|
|
651
655
|
uploadHost,
|
|
652
656
|
cache,
|
|
653
657
|
maxCacheSize,
|
|
654
|
-
syncHost,
|
|
655
658
|
});
|
|
656
659
|
}
|
package/dist/raw.d.ts
CHANGED
|
@@ -216,7 +216,7 @@ export interface CommonDocumentContent {
|
|
|
216
216
|
/** a mapping from page number to page id in pages */
|
|
217
217
|
redirectionPageMap?: number[];
|
|
218
218
|
/** ostensibly the size in bytes of the file, but this differs from other measurements */
|
|
219
|
-
sizeInBytes
|
|
219
|
+
sizeInBytes?: string;
|
|
220
220
|
/** text alignment for this document */
|
|
221
221
|
textAlignment: TextAlignment;
|
|
222
222
|
/**
|
|
@@ -560,7 +560,7 @@ export interface RawRemarkableApi {
|
|
|
560
560
|
*
|
|
561
561
|
* @returns the new list entry and a promise to finish the upload
|
|
562
562
|
*/
|
|
563
|
-
putEntries(id: string, entries: RawEntry[], schemaVersion: SchemaVersion): Promise<[RawEntry, Promise<void>]>;
|
|
563
|
+
putEntries(id: string, entries: readonly RawEntry[], schemaVersion: SchemaVersion): Promise<[RawEntry, Promise<void>]>;
|
|
564
564
|
/**
|
|
565
565
|
* upload a file to the reMarkable cloud using the simple api
|
|
566
566
|
*
|
|
@@ -603,7 +603,7 @@ export declare class RawRemarkable implements RawRemarkableApi {
|
|
|
603
603
|
putText(id: string, text: string): Promise<[RawEntry, Promise<void>]>;
|
|
604
604
|
putContent(id: string, content: Content): Promise<[RawEntry, Promise<void>]>;
|
|
605
605
|
putMetadata(id: string, metadata: Metadata): Promise<[RawEntry, Promise<void>]>;
|
|
606
|
-
putEntries(id: string, entries: RawEntry[], schemaVersion: SchemaVersion): Promise<[RawEntry, Promise<void>]>;
|
|
606
|
+
putEntries(id: string, entries: readonly RawEntry[], schemaVersion: SchemaVersion): Promise<[RawEntry, Promise<void>]>;
|
|
607
607
|
uploadFile(visibleName: string, bytes: Uint8Array, mime: UploadMimeType): Promise<SimpleEntry>;
|
|
608
608
|
dumpCache(): string;
|
|
609
609
|
clearCache(): void;
|