rmapi-js 9.0.3 → 10.0.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
@@ -228,10 +228,11 @@ export interface RemarkableApi {
228
228
  * the low-level api to get the raw text of the `.content` file in the
229
229
  * `RawEntry` for this hash.
230
230
  *
231
+ * @param id - the id of the item (as returned by `listIds`)
231
232
  * @param hash - the hash of the item to get content for
232
233
  * @returns the content
233
234
  */
234
- getContent(hash: string): Promise<Content>;
235
+ getContent(id: string, hash: string): Promise<Content>;
235
236
  /**
236
237
  * get the metadata from an item hash
237
238
  *
@@ -243,30 +244,33 @@ export interface RemarkableApi {
243
244
  * the low-level api to get the raw text of the `.metadata` file in the
244
245
  * `RawEntry` for this hash.
245
246
  *
247
+ * @param id - the id of the item (as returned by `listIds`)
246
248
  * @param hash - the hash of the item to get metadata for
247
249
  * @returns the metadata
248
250
  */
249
- getMetadata(hash: string): Promise<Metadata>;
251
+ getMetadata(id: string, hash: string): Promise<Metadata>;
250
252
  /**
251
253
  * get the pdf associated with a document hash
252
254
  *
253
255
  * This returns the raw input pdf, not the rendered pdf with any markup.
254
256
  *
257
+ * @param id - the id of the document (as returned by `listIds`)
255
258
  * @param hash - the hash of the document to get the pdf for (e.g. the hash
256
259
  * received from `listItems`)
257
260
  * @returns the pdf bytes
258
261
  */
259
- getPdf(hash: string): Promise<Uint8Array>;
262
+ getPdf(id: string, hash: string): Promise<Uint8Array>;
260
263
  /**
261
264
  * get the epub associated with a document hash
262
265
  *
263
266
  * This returns the raw input epub if a document was created from an epub.
264
267
  *
265
- * @param hash - the hash of the document to get the pdf for (e.g. the hash
268
+ * @param id - the id of the document (as returned by `listIds`)
269
+ * @param hash - the hash of the document to get the epub for (e.g. the hash
266
270
  * received from `listItems`)
267
271
  * @returns the epub bytes
268
272
  */
269
- getEpub(hash: string): Promise<Uint8Array>;
273
+ getEpub(id: string, hash: string): Promise<Uint8Array>;
270
274
  /**
271
275
  * get the entire contents of a remarkable document
272
276
  *
@@ -278,10 +282,11 @@ export interface RemarkableApi {
278
282
  * of the document, but this format isn't understood enoguh to reput this on a
279
283
  * different remarkable, so that functionality is currently disabled.
280
284
  *
281
- * @param hash - the hash of the document to get the contents for (e.g. the
285
+ * @param id - the id of the document (as returned by `listIds`)
286
+ * @param hash - the hash of the document to get contents for (e.g. the
282
287
  * hash received from `listItems`)
283
288
  */
284
- getDocument(hash: string): Promise<Uint8Array>;
289
+ getDocument(id: string, hash: string): Promise<Uint8Array>;
285
290
  /**
286
291
  * use the low-level api to add a pdf document
287
292
  *
package/dist/index.js CHANGED
@@ -180,18 +180,18 @@ class Remarkable {
180
180
  }
181
181
  }
182
182
  async #convertEntry({ hash, id }) {
183
- const { entries } = await this.raw.getEntries(hash);
183
+ const { entries } = await this.raw.getEntries(`${id}.docSchema`, hash);
184
184
  const metaEnt = entries.find((ent) => ent.id.endsWith(".metadata"));
185
185
  const contentEnt = entries.find((ent) => ent.id.endsWith(".content"));
186
186
  if (metaEnt === undefined) {
187
187
  throw new Error(`couldn't find metadata for hash ${hash}`);
188
188
  }
189
189
  const [{ visibleName, lastModified, pinned, parent, lastOpened, new: isNew, source, }, content,] = await Promise.all([
190
- this.raw.getMetadata(metaEnt.hash),
190
+ this.raw.getMetadata(metaEnt.id, metaEnt.hash),
191
191
  // collections don't always have content, since content only lists tags
192
192
  contentEnt === undefined
193
193
  ? Promise.resolve({ fileType: undefined, tags: undefined })
194
- : this.raw.getContent(contentEnt.hash),
194
+ : this.raw.getContent(contentEnt.id, contentEnt.hash),
195
195
  ]);
196
196
  if ("templateVersion" in content) {
197
197
  return {
@@ -240,55 +240,55 @@ class Remarkable {
240
240
  }
241
241
  async listIds(refresh = false) {
242
242
  const [hash] = await this.#getRootHash(refresh);
243
- const { entries } = await this.raw.getEntries(hash);
243
+ const { entries } = await this.raw.getEntries("root.docSchema", hash);
244
244
  return entries.map(({ id, hash }) => ({ id, hash }));
245
245
  }
246
- async getContent(hash) {
247
- const { entries } = await this.raw.getEntries(hash);
246
+ async getContent(id, hash) {
247
+ const { entries } = await this.raw.getEntries(`${id}.docSchema`, hash);
248
248
  const [cont] = entries.filter((e) => e.id.endsWith(".content"));
249
249
  if (cont === undefined) {
250
250
  throw new Error(`couldn't find contents for hash ${hash}`);
251
251
  }
252
252
  else {
253
- return await this.raw.getContent(cont.hash);
253
+ return await this.raw.getContent(cont.id, cont.hash);
254
254
  }
255
255
  }
256
- async getMetadata(hash) {
257
- const { entries } = await this.raw.getEntries(hash);
256
+ async getMetadata(id, hash) {
257
+ const { entries } = await this.raw.getEntries(`${id}.docSchema`, hash);
258
258
  const [meta] = entries.filter((e) => e.id.endsWith(".metadata"));
259
259
  if (meta === undefined) {
260
260
  throw new Error(`couldn't find metadata for hash ${hash}`);
261
261
  }
262
262
  else {
263
- return await this.raw.getMetadata(meta.hash);
263
+ return await this.raw.getMetadata(meta.id, meta.hash);
264
264
  }
265
265
  }
266
- async getPdf(hash) {
267
- const { entries } = await this.raw.getEntries(hash);
266
+ async getPdf(id, hash) {
267
+ const { entries } = await this.raw.getEntries(`${id}.docSchema`, hash);
268
268
  const [pdf] = entries.filter((e) => e.id.endsWith(".pdf"));
269
269
  if (pdf === undefined) {
270
270
  throw new Error(`couldn't find pdf for hash ${hash}`);
271
271
  }
272
272
  else {
273
- return await this.raw.getHash(pdf.hash);
273
+ return await this.raw.getHash(pdf.id, pdf.hash);
274
274
  }
275
275
  }
276
- async getEpub(hash) {
277
- const { entries } = await this.raw.getEntries(hash);
276
+ async getEpub(id, hash) {
277
+ const { entries } = await this.raw.getEntries(`${id}.docSchema`, hash);
278
278
  const [epub] = entries.filter((e) => e.id.endsWith(".epub"));
279
279
  if (epub === undefined) {
280
280
  throw new Error(`couldn't find epub for hash ${hash}`);
281
281
  }
282
282
  else {
283
- return await this.raw.getHash(epub.hash);
283
+ return await this.raw.getHash(epub.id, epub.hash);
284
284
  }
285
285
  }
286
- async getDocument(hash) {
287
- const { entries } = await this.raw.getEntries(hash);
286
+ async getDocument(id, hash) {
287
+ const { entries } = await this.raw.getEntries(`${id}.docSchema`, hash);
288
288
  const zip = new JSZip();
289
289
  for (const entry of entries) {
290
290
  // TODO if this is .metadata we might want to assert type === "DocumentType"
291
- zip.file(entry.id, this.raw.getHash(entry.hash));
291
+ zip.file(entry.id, this.raw.getHash(entry.id, entry.hash));
292
292
  }
293
293
  return zip.generateAsync({ type: "uint8array" });
294
294
  }
@@ -344,7 +344,7 @@ class Remarkable {
344
344
  // now fetch root entries and upload this file entry
345
345
  const [[collectionEntry, uploadCollection], { entries: rootEntries }] = await Promise.all([
346
346
  this.raw.putEntries(id, [contentEntry, metadataEntry, pagedataEntry, fileEntry], schemaVersion),
347
- this.raw.getEntries(rootHash),
347
+ this.raw.getEntries("root.docSchema", rootHash),
348
348
  ]);
349
349
  // now upload a new root entry
350
350
  rootEntries.push(collectionEntry);
@@ -398,7 +398,7 @@ class Remarkable {
398
398
  // now fetch root entries and upload this file entry
399
399
  const [[collectionEntry, uploadCollection], { entries: rootEntries }] = await Promise.all([
400
400
  this.raw.putEntries(id, [contentEntry, metadataEntry], schemaVersion),
401
- this.raw.getEntries(rootHash),
401
+ this.raw.getEntries("root.docSchema", rootHash),
402
402
  ]);
403
403
  // now upload a new root entry
404
404
  rootEntries.push(collectionEntry);
@@ -428,13 +428,13 @@ class Remarkable {
428
428
  }
429
429
  /** edit just a content entry */
430
430
  async #editContentRaw(id, hash, update, schemaVersion) {
431
- const { entries } = await this.raw.getEntries(hash);
431
+ const { entries } = await this.raw.getEntries(`${id}.docSchema`, hash);
432
432
  const contInd = entries.findIndex((ent) => ent.id.endsWith(".content"));
433
433
  const contEntry = entries[contInd];
434
434
  if (contEntry === undefined) {
435
435
  throw new Error("internal error: couldn't find content in entry hash");
436
436
  }
437
- const cont = await this.raw.getContent(contEntry.hash);
437
+ const cont = await this.raw.getContent(contEntry.id, contEntry.hash);
438
438
  Object.assign(cont, update);
439
439
  const [newContEntry, uploadCont] = await this.raw.putContent(contEntry.id, cont);
440
440
  entries[contInd] = newContEntry;
@@ -445,7 +445,7 @@ class Remarkable {
445
445
  /** fully sync a content edit */
446
446
  async #editContent(hash, update, expectedType, refresh) {
447
447
  const [rootHash, generation, schemaVersion] = await this.#getRootHash(refresh);
448
- const { entries } = await this.raw.getEntries(rootHash);
448
+ const { entries } = await this.raw.getEntries("root.docSchema", rootHash);
449
449
  const hashInd = entries.findIndex((ent) => ent.hash === hash);
450
450
  const hashEnt = entries[hashInd];
451
451
  if (hashEnt === undefined) {
@@ -453,7 +453,7 @@ class Remarkable {
453
453
  }
454
454
  const [[newEnt, uploadEnt], meta] = await Promise.all([
455
455
  this.#editContentRaw(hashEnt.id, hash, update, schemaVersion),
456
- this.getMetadata(hash),
456
+ this.getMetadata(hashEnt.id, hash),
457
457
  ]);
458
458
  if (meta.type !== expectedType) {
459
459
  throw new Error(`expected type ${expectedType} but got ${meta.type} for hash ${hash}`);
@@ -477,13 +477,13 @@ class Remarkable {
477
477
  return await this.#editContent(hash, content, "TemplateType", refresh);
478
478
  }
479
479
  async #editMetaRaw(id, hash, update, schemaVersion) {
480
- const { entries } = await this.raw.getEntries(hash);
480
+ const { entries } = await this.raw.getEntries(`${id}.docSchema`, hash);
481
481
  const metaInd = entries.findIndex((ent) => ent.id.endsWith(".metadata"));
482
482
  const metaEntry = entries[metaInd];
483
483
  if (metaEntry === undefined) {
484
484
  throw new Error("internal error: couldn't find metadata in entry hash");
485
485
  }
486
- const meta = await this.raw.getMetadata(metaEntry.hash);
486
+ const meta = await this.raw.getMetadata(metaEntry.id, metaEntry.hash);
487
487
  Object.assign(meta, update);
488
488
  const [newMetaEntry, uploadMeta] = await this.raw.putMetadata(metaEntry.id, meta);
489
489
  entries[metaInd] = newMetaEntry;
@@ -493,7 +493,7 @@ class Remarkable {
493
493
  }
494
494
  async #editMeta(hash, update, refresh = false) {
495
495
  const [rootHash, generation, schemaVersion] = await this.#getRootHash(refresh);
496
- const { entries } = await this.raw.getEntries(rootHash);
496
+ const { entries } = await this.raw.getEntries("root.docSchema", rootHash);
497
497
  const hashInd = entries.findIndex((ent) => ent.hash === hash);
498
498
  const hashEnt = entries[hashInd];
499
499
  if (hashEnt === undefined) {
@@ -531,7 +531,7 @@ class Remarkable {
531
531
  throw new ValidationError(parent, idReg, "parent must be a valid document id");
532
532
  }
533
533
  const [rootHash, generation, schemaVersion] = await this.#getRootHash(refresh);
534
- const { entries } = await this.raw.getEntries(rootHash);
534
+ const { entries } = await this.raw.getEntries("root.docSchema", rootHash);
535
535
  const hashSet = new Set(hashes);
536
536
  const toUpdate = [];
537
537
  const newEntries = [];
@@ -568,15 +568,15 @@ class Remarkable {
568
568
  // should only go one step) to track all hashes encountered
569
569
  // NOTE that we could increase the cache in this process, or it's possible
570
570
  // for other calls to increase the cache with misc values.
571
- const base = await this.raw.getEntries(rootHash);
571
+ const base = await this.raw.getEntries("root.docSchema", rootHash);
572
572
  let entries = [base.entries];
573
573
  let nextEntries = [];
574
574
  while (entries.length) {
575
575
  for (const entryList of entries) {
576
- for (const { hash, type } of entryList) {
576
+ for (const { hash, type, id } of entryList) {
577
577
  toDelete.add(hash);
578
578
  if (type === 80000000) {
579
- nextEntries.push(this.raw.getEntries(hash));
579
+ nextEntries.push(this.raw.getEntries(`${id}.docSchema`, hash));
580
580
  }
581
581
  }
582
582
  }
package/dist/raw.d.ts CHANGED
@@ -431,50 +431,58 @@ export interface RawRemarkableApi {
431
431
  /**
432
432
  * get the raw binary data associated with a hash
433
433
  *
434
+ * @param fileName - the logical file name (`<id>.<ext>` for files, or
435
+ * `<id>.docSchema` / `"root.docSchema"` for entry indexes). reMarkable
436
+ * validates this against the rm-filename header.
434
437
  * @param hash - the hash to get the data for
435
438
  * @returns the data
436
439
  */
437
- getHash(hash: string): Promise<Uint8Array>;
440
+ getHash(fileName: string, hash: string): Promise<Uint8Array>;
438
441
  /**
439
442
  * get raw text data associated with a hash
440
443
  *
441
444
  * We assume text data are small, and so cache the entire text. If you want to
442
445
  * avoid this, use {@link getHash | `getHash`} combined with a TextDecoder.
443
446
 
447
+ * @param fileName - the logical file name (see {@link getHash})
444
448
  * @param hash - the hash to get text for
445
449
  * @returns the text
446
450
  */
447
- getText(hash: string): Promise<string>;
451
+ getText(fileName: string, hash: string): Promise<string>;
448
452
  /**
449
453
  * get the entries associated with a list hash
450
454
  *
451
455
  * A list hash is the root hash, or any hash with the type 80000000. NOTE
452
456
  * these are hashed differently than files.
453
457
 
458
+ * @param fileName - `"root.docSchema"` for the root, or `"<id>.docSchema"`
459
+ * for a sub-document's entry index
454
460
  * @param hash - the hash to get entries for
455
461
  * @returns the entries
456
462
  */
457
- getEntries(hash: string): Promise<Entries>;
463
+ getEntries(fileName: string, hash: string): Promise<Entries>;
458
464
  /**
459
465
  * get the parsed and validated `Content` of a content hash
460
466
  *
461
467
  * Use {@link getText | `getText`} combined with `JSON.parse` to bypass
462
468
  * validation
463
469
 
470
+ * @param fileName - typically `"<id>.content"`
464
471
  * @param hash - the hash to get Content for
465
472
  * @returns the content
466
473
  */
467
- getContent(hash: string): Promise<Content>;
474
+ getContent(fileName: string, hash: string): Promise<Content>;
468
475
  /**
469
476
  * get the parsed and validated `Metadata` of a metadata hash
470
477
  *
471
478
  * Use {@link getText | `getText`} combined with `JSON.parse` to bypass
472
479
  * validation
473
480
 
481
+ * @param fileName - typically `"<id>.metadata"`
474
482
  * @param hash - the hash to get Metadata for
475
483
  * @returns the metadata
476
484
  */
477
- getMetadata(hash: string): Promise<Metadata>;
485
+ getMetadata(fileName: string, hash: string): Promise<Metadata>;
478
486
  /**
479
487
  * update the current root hash
480
488
  *
@@ -564,11 +572,11 @@ export declare class RawRemarkable implements RawRemarkableApi {
564
572
  constructor(authedFetch: AuthedFetch, cache: Map<string, string | null>, rawHost: string, uploadHost: string);
565
573
  /** make an authorized request to remarkable */
566
574
  getRootHash(): Promise<[string, number, SchemaVersion]>;
567
- getHash(hash: string): Promise<Uint8Array>;
568
- getText(hash: string): Promise<string>;
569
- getEntries(hash: string): Promise<Entries>;
570
- getContent(hash: string): Promise<Content>;
571
- getMetadata(hash: string): Promise<Metadata>;
575
+ getHash(fileName: string, hash: string): Promise<Uint8Array>;
576
+ getText(fileName: string, hash: string): Promise<string>;
577
+ getEntries(fileName: string, hash: string): Promise<Entries>;
578
+ getContent(fileName: string, hash: string): Promise<Content>;
579
+ getMetadata(fileName: string, hash: string): Promise<Metadata>;
572
580
  putRootHash(hash: string, generation: number, broadcast?: boolean): Promise<[string, number]>;
573
581
  putFile(id: string, bytes: Uint8Array): Promise<[RawEntry, Promise<void>]>;
574
582
  putText(id: string, text: string): Promise<[RawEntry, Promise<void>]>;
package/dist/raw.js CHANGED
@@ -222,23 +222,23 @@ export class RawRemarkable {
222
222
  return [hash, generation, schemaVersion];
223
223
  }
224
224
  }
225
- async #getHash(hash) {
225
+ async #getHash(fileName, hash) {
226
226
  if (!hashReg.test(hash)) {
227
227
  throw new ValidationError(hash, hashReg, "hash was not a valid hash");
228
228
  }
229
- const resp = await this.#authedFetch("GET", `${this.#rawHost}/sync/v3/files/${hash}`);
229
+ const resp = await this.#authedFetch("GET", `${this.#rawHost}/sync/v3/files/${hash}`, { headers: { "rm-filename": fileName } });
230
230
  // TODO switch to `.bytes()`.
231
231
  const raw = await resp.arrayBuffer();
232
232
  return new Uint8Array(raw);
233
233
  }
234
- async getHash(hash) {
234
+ async getHash(fileName, hash) {
235
235
  const cached = this.#cache.get(hash);
236
236
  if (cached != null) {
237
237
  const enc = new TextEncoder();
238
238
  return enc.encode(cached);
239
239
  }
240
240
  else {
241
- const res = await this.#getHash(hash);
241
+ const res = await this.#getHash(fileName, hash);
242
242
  // mark that we know hash exists
243
243
  const cacheVal = this.#cache.get(hash);
244
244
  if (cacheVal === undefined) {
@@ -247,22 +247,22 @@ export class RawRemarkable {
247
247
  return res;
248
248
  }
249
249
  }
250
- async getText(hash) {
250
+ async getText(fileName, hash) {
251
251
  const cached = this.#cache.get(hash);
252
252
  if (cached != null) {
253
253
  return cached;
254
254
  }
255
255
  else {
256
256
  // NOTE two simultaneous requests will fetch twice
257
- const raw = await this.#getHash(hash);
257
+ const raw = await this.#getHash(fileName, hash);
258
258
  const dec = new TextDecoder();
259
259
  const res = dec.decode(raw);
260
260
  this.#cache.set(hash, res);
261
261
  return res;
262
262
  }
263
263
  }
264
- async getEntries(hash) {
265
- const rawFile = await this.getText(hash);
264
+ async getEntries(fileName, hash) {
265
+ const rawFile = await this.getText(fileName, hash);
266
266
  const [version, ...rest] = rawFile.slice(0, -1).split("\n");
267
267
  if (version === "3") {
268
268
  return { entries: rest.map(parseRawEntryLine) };
@@ -290,8 +290,8 @@ export class RawRemarkable {
290
290
  throw new Error(`schema version ${version} not supported`);
291
291
  }
292
292
  }
293
- async getContent(hash) {
294
- const raw = await this.getText(hash);
293
+ async getContent(fileName, hash) {
294
+ const raw = await this.getText(fileName, hash);
295
295
  const loaded = JSON.parse(raw);
296
296
  // jtd can't verify non-discriminated unions, in this case, we have fileType
297
297
  // defined or not. As a result, we try each, and concatenate the errors at the end
@@ -313,8 +313,8 @@ export class RawRemarkable {
313
313
  const joined = errors.join("\n\nor\n\n");
314
314
  throw new Error(`invalid content: ${joined}`);
315
315
  }
316
- async getMetadata(hash) {
317
- const raw = await this.getText(hash);
316
+ async getMetadata(fileName, hash) {
317
+ const raw = await this.getText(fileName, hash);
318
318
  const loaded = JSON.parse(raw);
319
319
  if (!metadata.guardAssert(loaded))
320
320
  throw Error("invalid metadata");
@@ -345,7 +345,7 @@ export class RawRemarkable {
345
345
  throw new Error(`new generation ${newGen} was not a safe integer; please file a bug report`);
346
346
  }
347
347
  }
348
- async #putFile(hash, fileName, bytes) {
348
+ async #putFile(fileName, hash, bytes) {
349
349
  // if the hash is already in the cache, writing is pointless
350
350
  if (!this.#cache.has(hash)) {
351
351
  const crc = CRC32C.buf(bytes, 0);
@@ -376,7 +376,7 @@ export class RawRemarkable {
376
376
  subfiles: 0,
377
377
  size: bytes.length,
378
378
  };
379
- return [res, this.#putFile(hash, id, bytes)];
379
+ return [res, this.#putFile(id, hash, bytes)];
380
380
  }
381
381
  async putText(id, text) {
382
382
  const enc = new TextEncoder();
@@ -444,11 +444,7 @@ export class RawRemarkable {
444
444
  subfiles: entries.length,
445
445
  size,
446
446
  };
447
- return [
448
- res,
449
- // NOTE when monitoring requests, this had the extension .docSchema appended, but I'm not entirely sure why
450
- this.#putFile(hash, `${id}.docSchema`, entryBuff),
451
- ];
447
+ return [res, this.#putFile(`${id}.docSchema`, hash, entryBuff)];
452
448
  }
453
449
  async uploadFile(visibleName, bytes, mime) {
454
450
  const enc = new TextEncoder();