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 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 api = session(sessionToken);
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 pdf, using the hash (from listItems)
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 { HashNotFoundError, ValidationError } from "./error";
58
- import { LruCache } from "./lru";
59
- import { RawRemarkable, } from "./raw";
60
- export { HashNotFoundError, ValidationError } from "./error";
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
- this.#lastHashGen = await this.raw.getRootHash();
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
- const [, , schemaVersion] = this.#lastHashGen; // guaranteed to be set
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
- // the keys to delete, we'll drop every key we can currently reach
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, type, id } of entryList) {
577
- toDelete.add(hash);
578
- if (type === 80000000) {
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 = values(nullable(string()));
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
- if (cached.guard(initCache)) {
628
- const entries = Object.entries(initCache);
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, syncHost } = options ?? {};
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: string;
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;