rmapi-js 8.0.1 → 8.1.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
@@ -99,8 +99,19 @@ export interface DocumentType extends EntryCommon {
99
99
  /** the timestamp of the last time this entry was opened */
100
100
  lastOpened: string;
101
101
  }
102
+ /** a template, such as from methods.remarkable.com */
103
+ export interface TemplateType extends EntryCommon {
104
+ /** the key to identify this as a template */
105
+ type: "TemplateType";
106
+ /** the timestamp of when the template was added/created */
107
+ createdTime?: string;
108
+ /** where this template was installed from */
109
+ source?: string;
110
+ /** indicates if this is a newly-installed template */
111
+ new?: boolean;
112
+ }
102
113
  /** a remarkable entry for cloud items */
103
- export type Entry = CollectionEntry | DocumentType;
114
+ export type Entry = CollectionEntry | DocumentType | TemplateType;
104
115
  /** an simple entry without any extra information */
105
116
  export interface SimpleEntry {
106
117
  /** the document id */
@@ -360,8 +371,45 @@ export interface DocumentContent {
360
371
  */
361
372
  viewBackgroundFilter?: BackgroundFilter;
362
373
  }
374
+ /**
375
+ * content metadata, stored with the "content" extension
376
+ *
377
+ * This largely contains description of how to render the document, rather than
378
+ * metadata about it.
379
+ */
380
+ export interface TemplateContent {
381
+ /** the template name */
382
+ name: string;
383
+ /** the template's author */
384
+ author: string;
385
+ /** Base64-encoded SVG icon image */
386
+ iconData: string;
387
+ /** category names this template belongs to (eg: "Planning", "Productivity") */
388
+ categories: string[];
389
+ /** labels associated with this template (eg: "Project management") */
390
+ labels: string[];
391
+ /** the orientation of this template */
392
+ orientation: "portrait" | "landscape";
393
+ /** semantic version for this template */
394
+ templateVersion: string;
395
+ /** template configuration format version (currently just `1`) */
396
+ formatVersion: number;
397
+ /**
398
+ * which screens the template supports:
399
+ *
400
+ * - `rm2`: reMarkable 2
401
+ * - `rmPP`: reMarkable Paper Pro
402
+ */
403
+ supportedScreens: ("rm2" | "rmPP")[];
404
+ /** constant values used by the commands in `items` */
405
+ constants?: {
406
+ [name: string]: number;
407
+ }[];
408
+ /** the template definition, an SVG-like DSL in JSON */
409
+ items: object[];
410
+ }
363
411
  /** content metadata for any item */
364
- export type Content = CollectionContent | DocumentContent;
412
+ export type Content = CollectionContent | DocumentContent | TemplateContent;
365
413
  /**
366
414
  * item level metadata
367
415
  *
@@ -399,7 +447,15 @@ export interface Metadata {
399
447
  * DocumentType is a document, an epub, pdf, or notebook, CollectionType is a
400
448
  * folder.
401
449
  */
402
- type: "DocumentType" | "CollectionType";
450
+ type: "DocumentType" | "CollectionType" | "TemplateType";
451
+ /** whether this is this a newly-installed template */
452
+ new?: boolean;
453
+ /**
454
+ * the provider from which this item was obtained/installed
455
+ *
456
+ * Example: a template from "com.remarkable.methods".
457
+ */
458
+ source?: string;
403
459
  /** [speculative] metadata version, always 0 */
404
460
  version?: number;
405
461
  /** the visible name of the item, what it's called on the reMarkable */
package/dist/index.js CHANGED
@@ -54,7 +54,7 @@
54
54
  import { fromByteArray } from "base64-js";
55
55
  import CRC32C from "crc-32/crc32c";
56
56
  import JSZip from "jszip";
57
- import { boolean, elements, enumeration, float64, int32, nullable, properties, string, timestamp, uint32, uint8, values, } from "jtd-ts";
57
+ import { boolean, elements, empty, enumeration, float64, int32, nullable, properties, string, timestamp, uint32, uint8, values, } from "jtd-ts";
58
58
  import { v4 as uuid4 } from "uuid";
59
59
  import { LruCache } from "./lru";
60
60
  const AUTH_HOST = "https://webapp-prod.cloud.remarkable.engineering";
@@ -201,6 +201,19 @@ const cPages = properties({
201
201
  const collectionContent = properties(undefined, {
202
202
  tags: elements(tag),
203
203
  });
204
+ const templateContent = properties({
205
+ name: string(),
206
+ author: string(),
207
+ iconData: string(),
208
+ categories: elements(string()),
209
+ labels: elements(string()),
210
+ orientation: enumeration("portrait", "landscape"),
211
+ templateVersion: string(),
212
+ formatVersion: uint8(),
213
+ supportedScreens: elements(enumeration("rm2", "rmPP")),
214
+ constants: elements(values(int32())),
215
+ items: elements(empty()),
216
+ });
204
217
  const documentContent = properties({
205
218
  coverPageNumber: int32(),
206
219
  documentMetadata,
@@ -253,7 +266,7 @@ const metadata = properties({
253
266
  lastModified: string(),
254
267
  parent: string(),
255
268
  pinned: boolean(),
256
- type: enumeration("DocumentType", "CollectionType"),
269
+ type: enumeration("DocumentType", "CollectionType", "TemplateType"),
257
270
  visibleName: string(),
258
271
  }, {
259
272
  lastOpened: string(),
@@ -399,18 +412,24 @@ class RawRemarkable {
399
412
  const raw = await this.getText(hash);
400
413
  const loaded = JSON.parse(raw);
401
414
  // jtd can't verify non-discriminated unions, in this case, we have fileType
402
- // defined or not. As a result, we only do a normal guard for the presence
403
- // of tags (e.g. empty content or only specify tags). Otherwise we'll throw
404
- // the full error for the richer content.
405
- if (collectionContent.guard(loaded)) {
406
- return loaded;
407
- }
408
- else if (documentContent.guardAssert(loaded)) {
409
- return loaded;
410
- }
411
- else {
412
- throw Error("invalid content");
415
+ // defined or not. As a result, we try each, and concatenate the errors at the end
416
+ const errors = [];
417
+ for (const [name, valid] of [
418
+ ["collection", collectionContent],
419
+ ["template", templateContent],
420
+ ["document", documentContent],
421
+ ]) {
422
+ try {
423
+ if (valid.guardAssert(loaded))
424
+ return loaded;
425
+ }
426
+ catch (ex) {
427
+ const msg = ex instanceof Error ? ex.message : "unknown error type";
428
+ errors.push(`Couldn't validate as ${name} because:\n${msg}`);
429
+ }
413
430
  }
431
+ const joined = errors.join("\n\nor\n\n");
432
+ throw new Error(`invalid content: ${joined}`);
414
433
  }
415
434
  async getMetadata(hash) {
416
435
  const raw = await this.getText(hash);
@@ -604,11 +623,24 @@ class Remarkable {
604
623
  else if (contentEnt === undefined) {
605
624
  throw new Error(`couldn't find content for hash ${hash}`);
606
625
  }
607
- const [{ visibleName, lastModified, pinned, parent, lastOpened }, content] = await Promise.all([
626
+ const [{ visibleName, lastModified, pinned, parent, lastOpened, new: isNew, source, }, content,] = await Promise.all([
608
627
  this.raw.getMetadata(metaEnt.hash),
609
628
  this.raw.getContent(contentEnt.hash),
610
629
  ]);
611
- if (content.fileType === undefined) {
630
+ if ("templateVersion" in content) {
631
+ return {
632
+ id,
633
+ hash,
634
+ visibleName,
635
+ lastModified,
636
+ new: isNew,
637
+ pinned,
638
+ source,
639
+ parent,
640
+ type: "TemplateType",
641
+ };
642
+ }
643
+ else if (content.fileType === undefined) {
612
644
  return {
613
645
  id,
614
646
  hash,