rmapi-js 8.4.0 → 8.5.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 CHANGED
@@ -21,19 +21,28 @@ and folders ("collections"). The hash indicates the full current state to manage
21
21
  ## Usage
22
22
 
23
23
  To explore files in the cloud, you need to first register your api and persist
24
- the token. Then you can use `listFiles` to explore entries of different file
24
+ the token. Then you can use `listItems` to explore entries of different file
25
25
  collections.
26
26
 
27
27
  ```ts
28
- import { register, remarkable } from "rmapi-js";
28
+ import { auth, register, remarkable, session } from "rmapi-js";
29
29
 
30
30
  const code = "..."; // eight letter code from https://my.remarkable.com/device/desktop/connect
31
31
  const token = await register(code);
32
32
  // persist token so you don't have to register again
33
33
  const api = await remarkable(token);
34
- const fileEntries = await api.listFiles();
34
+ const fileEntries = await api.listItems();
35
+
36
+ // In stateless environments, exchange once and reuse.
37
+ const sessionToken = await auth(token);
38
+ const api = session(sessionToken);
39
+ // cache `sessionToken` and reuse it across workers
35
40
  ```
36
41
 
42
+ `auth` performs the same network call that `remarkable` does for you internally,
43
+ returning a short-lived session token. `session` is synchronous,
44
+ letting you construct clients from cached tokens without making a network call.
45
+
37
46
  To upload an epub or pdf, simply call upload with the appropriate name and buffer.
38
47
 
39
48
  ```ts
@@ -53,7 +62,7 @@ Using these apis is a little riskier since they can potentially result in data l
53
62
  // upload with custom line height not avilable through reMarkable
54
63
  await api.putEpub("name", buffer, { lineHeight: 180 })
55
64
 
56
- // fetch an uploaded pdf, using the hash (from listFiles)
65
+ // fetch an uploaded pdf, using the hash (from listItems)
57
66
  const buffer = await api.getEpub(hash)
58
67
  ```
59
68
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
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, SimpleEntry, Tag, TemplateContent, TextAlignment, UploadMimeType, ZoomMode, } from "./raw";
3
+ export type { BackgroundFilter, CollectionContent, Content, CPageNumberValue, CPagePage, CPageStringValue, CPages, 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 */
@@ -482,14 +482,17 @@ export interface RemarkableApi {
482
482
  */
483
483
  clearCache(): void;
484
484
  }
485
- /** options for a remarkable instance */
486
- export interface RemarkableOptions {
485
+ /** configuration for exchanging a device token */
486
+ export interface AuthOptions {
487
487
  /**
488
488
  * the url for making authorization requests
489
489
  *
490
490
  * @defaultValue "https://webapp-prod.cloud.remarkable.engineering"
491
491
  */
492
492
  authHost?: string;
493
+ }
494
+ /** options for constructing an api instance from a session token */
495
+ export interface RemarkableSessionOptions {
493
496
  /**
494
497
  * the url for making synchronization requests
495
498
  *
@@ -526,14 +529,35 @@ export interface RemarkableOptions {
526
529
  */
527
530
  maxCacheSize?: number;
528
531
  }
532
+ /** options for a remarkable instance */
533
+ export interface RemarkableOptions extends AuthOptions, RemarkableSessionOptions {
534
+ }
535
+ /**
536
+ * Exchange a device token for a session token.
537
+ *
538
+ * @param deviceToken - the device token proving this api instance is
539
+ * registered. Create one with {@link register}.
540
+ * @returns the session token returned by the reMarkable service
541
+ */
542
+ export declare function auth(deviceToken: string, { authHost }?: AuthOptions): Promise<string>;
543
+ /**
544
+ * Create an API instance from an existing session token.
545
+ *
546
+ * If requests start failing, simply recreate the api instance with a freshly
547
+ * fetched session token.
548
+ *
549
+ * @param sessionToken - the session token used for authorization
550
+ * @returns an api instance
551
+ */
552
+ export declare function session(sessionToken: string, { rawHost, uploadHost, cache, maxCacheSize, }?: RemarkableSessionOptions): RemarkableApi;
529
553
  /**
530
554
  * create an instance of the api
531
555
  *
532
- * This gets a temporary authentication token with the device token. If
533
- * requests start failing, simply recreate the api instance.
556
+ * This gets a temporary authentication token with the device token and then
557
+ * constructs the api instance.
534
558
  *
535
559
  * @param deviceToken - the device token proving this api instance is
536
560
  * registered. Create one with {@link register}.
537
561
  * @returns an api instance
538
562
  */
539
- export declare function remarkable(deviceToken: string, { authHost, rawHost, uploadHost, cache, maxCacheSize, }?: RemarkableOptions): Promise<RemarkableApi>;
563
+ export declare function remarkable(deviceToken: string, options?: RemarkableOptions): Promise<RemarkableApi>;
package/dist/index.js CHANGED
@@ -126,13 +126,13 @@ export async function register(code, { deviceDesc = "browser-chrome", uuid = uui
126
126
  }
127
127
  /** the implementation of that api */
128
128
  class Remarkable {
129
- #userToken;
129
+ #sessionToken;
130
130
  /** the same cache that underlies the raw api, allowing us to modify it */
131
131
  #cache;
132
132
  raw;
133
133
  #lastHashGen;
134
- constructor(userToken, rawHost, uploadHost, cache) {
135
- this.#userToken = userToken;
134
+ constructor(sessionToken, rawHost, uploadHost, cache) {
135
+ this.#sessionToken = sessionToken;
136
136
  this.#cache = cache;
137
137
  this.raw = new RawRemarkable((method, url, { body, headers } = {}) => this.#authedFetch(url, { method, body, headers }), cache, rawHost, uploadHost);
138
138
  }
@@ -158,7 +158,7 @@ class Remarkable {
158
158
  const resp = await fetch(url, {
159
159
  method,
160
160
  headers: {
161
- Authorization: `Bearer ${this.#userToken}`,
161
+ Authorization: `Bearer ${this.#sessionToken}`,
162
162
  ...headers,
163
163
  },
164
164
  // fetch works correctly with uint8 arrays, but is not hinted correctly
@@ -596,16 +596,13 @@ class Remarkable {
596
596
  }
597
597
  const cached = values(nullable(string()));
598
598
  /**
599
- * create an instance of the api
600
- *
601
- * This gets a temporary authentication token with the device token. If
602
- * requests start failing, simply recreate the api instance.
599
+ * Exchange a device token for a session token.
603
600
  *
604
601
  * @param deviceToken - the device token proving this api instance is
605
602
  * registered. Create one with {@link register}.
606
- * @returns an api instance
603
+ * @returns the session token returned by the reMarkable service
607
604
  */
608
- export async function remarkable(deviceToken, { authHost = AUTH_HOST, rawHost = RAW_HOST, uploadHost = UPLOAD_HOST, cache, maxCacheSize = Infinity, } = {}) {
605
+ export async function auth(deviceToken, { authHost = AUTH_HOST } = {}) {
609
606
  const resp = await fetch(`${authHost}/token/json/2/user/new`, {
610
607
  method: "POST",
611
608
  headers: {
@@ -615,16 +612,46 @@ export async function remarkable(deviceToken, { authHost = AUTH_HOST, rawHost =
615
612
  if (!resp.ok) {
616
613
  throw new Error(`couldn't fetch auth token: ${resp.statusText}`);
617
614
  }
618
- const userToken = await resp.text();
615
+ return await resp.text();
616
+ }
617
+ /**
618
+ * Create an API instance from an existing session token.
619
+ *
620
+ * If requests start failing, simply recreate the api instance with a freshly
621
+ * fetched session token.
622
+ *
623
+ * @param sessionToken - the session token used for authorization
624
+ * @returns an api instance
625
+ */
626
+ export function session(sessionToken, { rawHost = RAW_HOST, uploadHost = UPLOAD_HOST, cache, maxCacheSize = Infinity, } = {}) {
619
627
  const initCache = JSON.parse(cache ?? "{}");
620
628
  if (cached.guard(initCache)) {
621
629
  const entries = Object.entries(initCache);
622
- const cache = maxCacheSize === Infinity
630
+ const cacheMap = maxCacheSize === Infinity
623
631
  ? new Map(entries)
624
632
  : new LruCache(maxCacheSize, entries);
625
- return new Remarkable(userToken, rawHost, uploadHost, cache);
626
- }
627
- else {
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.");
633
+ return new Remarkable(sessionToken, rawHost, uploadHost, cacheMap);
629
634
  }
635
+ 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.");
636
+ }
637
+ /**
638
+ * create an instance of the api
639
+ *
640
+ * This gets a temporary authentication token with the device token and then
641
+ * constructs the api instance.
642
+ *
643
+ * @param deviceToken - the device token proving this api instance is
644
+ * registered. Create one with {@link register}.
645
+ * @returns an api instance
646
+ */
647
+ export async function remarkable(deviceToken, options = {}) {
648
+ const { authHost, rawHost, uploadHost, cache, maxCacheSize, syncHost } = options ?? {};
649
+ const sessionToken = await auth(deviceToken, { authHost });
650
+ return session(sessionToken, {
651
+ rawHost,
652
+ uploadHost,
653
+ cache,
654
+ maxCacheSize,
655
+ syncHost,
656
+ });
630
657
  }
package/dist/raw.d.ts CHANGED
@@ -61,7 +61,7 @@ export interface PageTag extends Tag {
61
61
  /** all supported document orientations */
62
62
  export type Orientation = "portrait" | "landscape";
63
63
  /** all supported text alignments */
64
- export type TextAlignment = "justify" | "left";
64
+ export type TextAlignment = "" | "justify" | "left";
65
65
  /** types of zoom modes for documents, applies primarily to pdf files */
66
66
  export type ZoomMode = "bestFit" | "customFit" | "fitToHeight" | "fitToWidth";
67
67
  /**
@@ -202,8 +202,8 @@ export interface DocumentContent {
202
202
  pageCount: number;
203
203
  /** the page tags for the document */
204
204
  pageTags?: PageTag[];
205
- /** a list of the ids of each page in the document */
206
- pages?: string[];
205
+ /** a list of the ids of each page in the document, or null when never opened */
206
+ pages?: string[] | null;
207
207
  /** a mapping from page number to page id in pages */
208
208
  redirectionPageMap?: number[];
209
209
  /** ostensibly the size in bytes of the file, but this differs from other measurements */
@@ -554,12 +554,10 @@ export interface RawRemarkableApi {
554
554
  /** completely clear the cache */
555
555
  clearCache(): void;
556
556
  }
557
- interface AuthedFetch {
558
- (method: RequestMethod, url: string, init?: {
559
- body?: string | Uint8Array;
560
- headers?: Record<string, string>;
561
- }): Promise<Response>;
562
- }
557
+ type AuthedFetch = (method: RequestMethod, url: string, init?: {
558
+ body?: string | Uint8Array;
559
+ headers?: Record<string, string>;
560
+ }) => Promise<Response>;
563
561
  export declare class RawRemarkable implements RawRemarkableApi {
564
562
  #private;
565
563
  constructor(authedFetch: AuthedFetch, cache: Map<string, string | null>, rawHost: string, uploadHost: string);
package/dist/raw.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { fromByteArray } from "base64-js";
2
2
  import CRC32C from "crc-32/crc32c";
3
- import { boolean, elements, empty, enumeration, float64, int32, properties, string, timestamp, uint32, uint8, values, } from "jtd-ts";
3
+ import { boolean, elements, empty, enumeration, float64, int32, nullable, properties, string, timestamp, uint8, uint32, values, } from "jtd-ts";
4
4
  import { ValidationError } from "./error.js";
5
5
  const hashReg = /^[0-9a-f]{64}$/;
6
6
  const tag = properties({
@@ -74,7 +74,7 @@ const documentContent = properties({
74
74
  orientation: enumeration("portrait", "landscape"),
75
75
  pageCount: uint32(),
76
76
  sizeInBytes: string(),
77
- textAlignment: enumeration("justify", "left"),
77
+ textAlignment: enumeration("", "justify", "left"),
78
78
  textScale: float64(),
79
79
  }, {
80
80
  cPages,
@@ -90,10 +90,10 @@ const documentContent = properties({
90
90
  count: uint32(),
91
91
  timestamp: float64(),
92
92
  }, undefined, true),
93
- lastOpenedPage: uint32(),
93
+ lastOpenedPage: int32(),
94
94
  margins: uint32(),
95
95
  originalPageCount: int32(),
96
- pages: elements(string()),
96
+ pages: nullable(elements(string())),
97
97
  pageTags: elements(pageTag),
98
98
  redirectionPageMap: elements(int32()),
99
99
  tags: elements(tag),
@@ -134,7 +134,7 @@ const metadata = properties({
134
134
  visibleName: string(),
135
135
  }, {
136
136
  lastOpened: string(),
137
- lastOpenedPage: uint32(),
137
+ lastOpenedPage: int32(),
138
138
  createdTime: string(),
139
139
  deleted: boolean(),
140
140
  metadatamodified: boolean(),
@@ -243,7 +243,7 @@ export class RawRemarkable {
243
243
  async getEntries(hash) {
244
244
  const rawFile = await this.getText(hash);
245
245
  const [version, ...rest] = rawFile.slice(0, -1).split("\n");
246
- if (version != "3") {
246
+ if (version !== "3") {
247
247
  throw new Error(`schema version ${version} not supported`);
248
248
  }
249
249
  else {
@@ -261,8 +261,8 @@ export class RawRemarkable {
261
261
  hash,
262
262
  type: 80000000,
263
263
  id,
264
- subfiles: parseInt(subfiles),
265
- size: parseInt(size),
264
+ subfiles: parseInt(subfiles, 10),
265
+ size: parseInt(size, 10),
266
266
  };
267
267
  }
268
268
  else if (type === "0" && subfiles === "0") {
@@ -271,7 +271,7 @@ export class RawRemarkable {
271
271
  type: 0,
272
272
  id,
273
273
  subfiles: 0,
274
- size: parseInt(size),
274
+ size: parseInt(size, 10),
275
275
  };
276
276
  }
277
277
  else {