rmapi-js 1.1.0 → 2.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.js CHANGED
@@ -1,12 +1,17 @@
1
1
  /**
2
2
  * Create and interact with reMarkable cloud.
3
3
  *
4
+ * After getting a device token with the {@link register | `register`} method,
5
+ * persist it and create api instances using {@link remarkable | `remarkable`}.
6
+ * Outside of registration, all relevant methods are in
7
+ * {@link RemarkableApi | `RemarkableApi`}.
8
+ *
4
9
  * @example
5
10
  * A simple fetch
6
11
  * ```ts
7
12
  * import { register, remarkable } from "rmapi-js";
8
13
  *
9
- * const code = "..." // eight letter code from https://my.remarkable.com/device/desktop/connect
14
+ * const code = "..." // eight letter code from https://my.remarkable.com/device/browser/connect
10
15
  * const token = await register(code)
11
16
  * // persist token
12
17
  * const api = await remarkable(token);
@@ -35,19 +40,22 @@
35
40
  * const rootEntries = await api.getEntries(root);
36
41
  * rootEntries.push(entry);
37
42
  * const { hash } = await api.putEntries("", rootEntries);
38
- * await api.putRootHash(hash, gen);
43
+ * const nextGen = await api.putRootHash(hash, gen);
44
+ * await api.syncComplete(nextGen);
39
45
  * ```
40
46
  *
41
47
  * @packageDocumentation
42
48
  */
49
+ import { fromByteArray } from "base64-js";
43
50
  import { v4 as uuid4 } from "uuid";
44
51
  import { concatBuffers, fromHex, toHex } from "./utils";
45
52
  import { validate } from "./validate";
46
53
  const SCHEMA_VERSION = "3";
47
- const AUTH_URL = "https://webapp-production-dot-remarkable-production.appspot.com";
48
- const BLOB_URL = "https://rm-blob-storage-prod.appspot.com";
54
+ const AUTH_HOST = "https://webapp-prod.cloud.remarkable.engineering";
55
+ const SYNC_HOST = "https://internal.cloud.remarkable.com";
49
56
  const GENERATION_HEADER = "x-goog-generation";
50
57
  const GENERATION_RACE_HEADER = "x-goog-if-generation-match";
58
+ const CONTENT_LENGTH_RANGE_HEADER = "x-goog-content-length-range";
51
59
  /** tool options */
52
60
  export const builtinTools = [
53
61
  "Ballpoint",
@@ -117,6 +125,12 @@ export const builtinLineHeights = {
117
125
  /** double */
118
126
  xl: 200,
119
127
  };
128
+ const uploadEntrySchema = {
129
+ properties: {
130
+ docID: { type: "string" },
131
+ hash: { type: "string" },
132
+ },
133
+ };
120
134
  const urlResponseSchema = {
121
135
  properties: {
122
136
  relative_path: { type: "string" },
@@ -124,19 +138,22 @@ const urlResponseSchema = {
124
138
  expires: { type: "timestamp" },
125
139
  method: { enum: ["POST", "GET", "PUT", "DELETE"] },
126
140
  },
141
+ optionalProperties: {
142
+ maxuploadsize_bytes: { type: "float64" },
143
+ },
127
144
  };
128
145
  const commonProperties = {
129
146
  visibleName: { type: "string" },
130
- parent: { type: "string" },
131
147
  lastModified: { type: "string" },
132
- version: { type: "int32" },
133
- synced: { type: "boolean" },
134
148
  };
135
149
  const commonOptionalProperties = {
150
+ version: { type: "int32" },
136
151
  pinned: { type: "boolean" },
152
+ synced: { type: "boolean" },
137
153
  modified: { type: "boolean" },
138
154
  deleted: { type: "boolean" },
139
155
  metadatamodified: { type: "boolean" },
156
+ parent: { type: "string" },
140
157
  };
141
158
  const metadataSchema = {
142
159
  discriminator: "type",
@@ -155,6 +172,34 @@ const metadataSchema = {
155
172
  },
156
173
  },
157
174
  };
175
+ const baseMetadataProperties = {
176
+ id: { type: "string" },
177
+ hash: { type: "string" },
178
+ };
179
+ const metadataEntrySchema = {
180
+ discriminator: "type",
181
+ mapping: {
182
+ CollectionType: {
183
+ properties: {
184
+ ...commonProperties,
185
+ ...baseMetadataProperties,
186
+ },
187
+ optionalProperties: commonOptionalProperties,
188
+ },
189
+ DocumentType: {
190
+ properties: {
191
+ ...commonProperties,
192
+ ...baseMetadataProperties,
193
+ fileType: { enum: ["notebook", "epub", "pdf", ""] },
194
+ },
195
+ optionalProperties: {
196
+ ...commonOptionalProperties,
197
+ lastOpened: { type: "string" },
198
+ lastOpenedPage: { type: "int32" },
199
+ },
200
+ },
201
+ },
202
+ };
158
203
  /** an error that results from a failed request */
159
204
  export class ResponseError extends Error {
160
205
  /** the response status number */
@@ -181,18 +226,18 @@ export class GenerationError extends Error {
181
226
  /**
182
227
  * register a device and get the token needed to access the api
183
228
  *
184
- * Have users go to `https://my.remarkable.com/device/desktop/connect` and pass
229
+ * Have users go to `https://my.remarkable.com/device/browser/connect` and pass
185
230
  * the resulting code into this function to get a device token. Persist that
186
231
  * token to use the api.
187
232
  *
188
- * @param code - the eight letter code a user got from `https://my.remarkable.com/device/desktop/connect`.
233
+ * @param code - the eight letter code a user got from `https://my.remarkable.com/device/browser/connect`.
189
234
  * @returns the device token necessary for creating an api instace. These never expire so persist as long as necessary.
190
235
  */
191
- export async function register(code, { deviceDesc = "desktop-linux", uuid = uuid4(), authUrl = AUTH_URL, fetch = globalThis.fetch, } = {}) {
236
+ export async function register(code, { deviceDesc = "browser-chrome", uuid = uuid4(), authHost = AUTH_HOST, fetch = globalThis.fetch, } = {}) {
192
237
  if (code.length !== 8) {
193
238
  throw new Error(`code should be length 8, but was ${code.length}`);
194
239
  }
195
- const resp = await fetch(`${authUrl}/token/json/2/device/new`, {
240
+ const resp = await fetch(`${authHost}/token/json/2/device/new`, {
196
241
  method: "POST",
197
242
  headers: {
198
243
  Authorization: "Bearer",
@@ -253,37 +298,45 @@ export function parseEntry(line) {
253
298
  }
254
299
  /** the implementation of that api */
255
300
  class Remarkable {
256
- userToken;
257
- fetch;
258
- cache;
259
- subtle;
260
- blobUrl;
261
- constructor(userToken, fetch, cache, subtle, blobUrl) {
262
- this.userToken = userToken;
263
- this.fetch = fetch;
264
- this.cache = cache;
265
- this.subtle = subtle;
266
- this.blobUrl = blobUrl;
301
+ #userToken;
302
+ #fetch;
303
+ #cache;
304
+ #subtle;
305
+ #syncHost;
306
+ constructor(userToken, fetch, cache, subtle, syncHost) {
307
+ this.#userToken = userToken;
308
+ this.#fetch = fetch;
309
+ this.#cache = cache;
310
+ this.#subtle = subtle;
311
+ this.#syncHost = syncHost;
267
312
  }
268
313
  /** make an authorized request to remarkable */
269
- async authedFetch(url, body, method = "POST") {
270
- const resp = await this.fetch(url, {
314
+ async #authedFetch(url, { body, method = "POST", headers = {}, }) {
315
+ const resp = await this.#fetch(url, {
271
316
  method,
272
317
  headers: {
273
- Authorization: `Bearer ${this.userToken}`,
318
+ Authorization: `Bearer ${this.#userToken}`,
319
+ ...headers,
274
320
  },
275
- body: body && JSON.stringify(body),
321
+ body,
276
322
  });
277
323
  if (!resp.ok) {
278
- throw new ResponseError(resp.status, resp.statusText, "failed reMarkable request");
324
+ const msg = await resp.text();
325
+ throw new ResponseError(resp.status, resp.statusText, `failed reMarkable request: ${msg}`);
279
326
  }
280
327
  else {
281
328
  return resp;
282
329
  }
283
330
  }
284
331
  /** make a signed request to the cloud */
285
- async signedFetch({ url, method }, body, headers) {
286
- const resp = await this.fetch(url, {
332
+ async #signedFetch({ url, method, maxuploadsize_bytes }, body, add_headers = {}) {
333
+ const headers = maxuploadsize_bytes
334
+ ? {
335
+ ...add_headers,
336
+ [CONTENT_LENGTH_RANGE_HEADER]: `0,${maxuploadsize_bytes}`,
337
+ }
338
+ : add_headers;
339
+ const resp = await this.#fetch(url, {
287
340
  method,
288
341
  body,
289
342
  headers,
@@ -297,14 +350,13 @@ class Remarkable {
297
350
  }
298
351
  }
299
352
  /** get the details for how to make a signed request to remarkable cloud */
300
- async getUrl(relativePath, gen) {
353
+ async #getUrl(relativePath, gen, rootHash) {
301
354
  const key = gen === undefined ? "downloads" : "uploads";
302
- const generation = gen === null || gen === undefined ? undefined : `${gen}`;
303
- const resp = await this.authedFetch(`${this.blobUrl}/api/v1/signed-urls/${key}`, {
304
- http_method: generation === undefined ? "GET" : "PUT",
305
- relative_path: relativePath,
306
- generation,
307
- });
355
+ // NOTE this is done manually to serialize the bigints appropriately
356
+ const body = rootHash
357
+ ? `{ "http_method": "PUT", "relative_path": "${relativePath}", "root_schema": "${rootHash}", "generation": ${gen} }`
358
+ : JSON.stringify({ http_method: "GET", relative_path: relativePath });
359
+ const resp = await this.#authedFetch(`${this.#syncHost}/sync/v2/signed-urls/${key}`, { body });
308
360
  const raw = await resp.text();
309
361
  const res = JSON.parse(raw);
310
362
  validate(urlResponseSchema, res);
@@ -314,8 +366,8 @@ class Remarkable {
314
366
  * get the root hash and the current generation
315
367
  */
316
368
  async getRootHash() {
317
- const signed = await this.getUrl("root");
318
- const resp = await this.signedFetch(signed);
369
+ const signed = await this.#getUrl("root");
370
+ const resp = await this.#signedFetch(signed);
319
371
  const generation = resp.headers.get(GENERATION_HEADER);
320
372
  if (!generation) {
321
373
  throw new Error("no generation header in root hash");
@@ -326,10 +378,10 @@ class Remarkable {
326
378
  * write the root hash, incrementing from the current generation
327
379
  */
328
380
  async putRootHash(hash, generation) {
329
- const signed = await this.getUrl("root", generation);
381
+ const signed = await this.#getUrl("root", generation, hash);
330
382
  let resp;
331
383
  try {
332
- resp = await this.signedFetch(signed, hash, {
384
+ resp = await this.#signedFetch(signed, hash, {
333
385
  [GENERATION_RACE_HEADER]: `${generation}`,
334
386
  });
335
387
  }
@@ -351,23 +403,23 @@ class Remarkable {
351
403
  * get text content associated with hash
352
404
  */
353
405
  async getBuffer(hash) {
354
- const signed = await this.getUrl(hash);
355
- const resp = await this.signedFetch(signed);
406
+ const signed = await this.#getUrl(hash);
407
+ const resp = await this.#signedFetch(signed);
356
408
  return await resp.arrayBuffer();
357
409
  }
358
410
  /**
359
411
  * get text content associated with hash
360
412
  */
361
413
  async getText(hash) {
362
- const cached = this.cache && (await this.cache.get(hash));
414
+ const cached = await this.#cache?.get(hash);
363
415
  if (cached) {
364
416
  return cached;
365
417
  }
366
418
  else {
367
- const signed = await this.getUrl(hash);
368
- const resp = await this.signedFetch(signed);
419
+ const signed = await this.#getUrl(hash);
420
+ const resp = await this.#signedFetch(signed);
369
421
  const raw = await resp.text();
370
- this.cache && (await this.cache.set(hash, raw));
422
+ await this.#cache?.set(hash, raw);
371
423
  return raw;
372
424
  }
373
425
  }
@@ -393,9 +445,9 @@ class Remarkable {
393
445
  return lines.map(parseEntry);
394
446
  }
395
447
  /** upload data to hash */
396
- async putHash(hash, body) {
397
- const signed = await this.getUrl(hash, null);
398
- await this.signedFetch(signed, body);
448
+ async #putHash(hash, body) {
449
+ const signed = await this.#getUrl(hash, null);
450
+ await this.#signedFetch(signed, body);
399
451
  }
400
452
  /** put a reference to a set of entries into the cloud */
401
453
  async putEntries(documentId, entries) {
@@ -403,13 +455,13 @@ class Remarkable {
403
455
  const enc = new TextEncoder();
404
456
  entries.sort((a, b) => a.documentId.localeCompare(b.documentId));
405
457
  const hashes = concatBuffers(entries.map((ent) => fromHex(ent.hash)));
406
- const digest = await this.subtle.digest("SHA-256", hashes);
458
+ const digest = await this.#subtle.digest("SHA-256", hashes);
407
459
  const hash = toHex(digest);
408
460
  const entryContents = entries.map(formatEntry).join("");
409
461
  const contents = `${SCHEMA_VERSION}\n${entryContents}`;
410
462
  const buffer = enc.encode(contents);
411
- await this.putHash(hash, buffer);
412
- this.cache && (await this.cache.set(hash, contents));
463
+ await this.#putHash(hash, buffer);
464
+ await this.#cache?.set(hash, contents);
413
465
  return {
414
466
  hash,
415
467
  type: "80000000",
@@ -420,9 +472,9 @@ class Remarkable {
420
472
  }
421
473
  /** put a raw buffer in the cloud */
422
474
  async putBuffer(documentId, buffer) {
423
- const digest = await this.subtle.digest("SHA-256", buffer);
475
+ const digest = await this.#subtle.digest("SHA-256", buffer);
424
476
  const hash = toHex(digest);
425
- await this.putHash(hash, buffer);
477
+ await this.#putHash(hash, buffer);
426
478
  return {
427
479
  hash,
428
480
  type: "0",
@@ -435,16 +487,43 @@ class Remarkable {
435
487
  async putText(documentId, contents) {
436
488
  const enc = new TextEncoder();
437
489
  const entry = await this.putBuffer(documentId, enc.encode(contents));
438
- this.cache && (await this.cache.set(entry.hash, contents));
490
+ await this.#cache?.set(entry.hash, contents);
439
491
  return entry;
440
492
  }
441
- /** upload an epub */
442
- async putEpub(visibleName, buffer, { parent = "", margins = 125, orientation, textAlignment, textScale = 1, lineHeight = -1, fontName = "", cover = "visited", lastTool, } = {}) {
493
+ /** put metadata into the cloud */
494
+ async putMetadata(documentId, metadata) {
495
+ return await this.putText(`${documentId}.metadata`, JSON.stringify(metadata));
496
+ }
497
+ /** put a new collection (folder) */
498
+ async putCollection(visibleName, parent = "") {
499
+ const documentId = uuid4();
500
+ const lastModified = `${new Date().valueOf()}`;
501
+ const entryPromises = [];
502
+ // upload metadata
503
+ const metadata = {
504
+ type: "CollectionType",
505
+ visibleName,
506
+ version: 0,
507
+ parent,
508
+ synced: true,
509
+ lastModified,
510
+ };
511
+ entryPromises.push(this.putMetadata(documentId, metadata));
512
+ entryPromises.push(this.putText(`${documentId}.content`, "{}"));
513
+ const entries = await Promise.all(entryPromises);
514
+ return await this.putEntries(documentId, entries);
515
+ }
516
+ /** upload a content file */
517
+ async #putContent(visibleName, buffer, fileType, parent, content) {
518
+ /* istanbul ignore if */
519
+ if (content.fileType !== fileType) {
520
+ throw new Error(`internal error: fileTypes don't match: ${fileType}, ${content.fileType}`);
521
+ }
443
522
  const documentId = uuid4();
444
523
  const lastModified = `${new Date().valueOf()}`;
445
524
  const entryPromises = [];
446
525
  // upload main document
447
- entryPromises.push(this.putBuffer(`${documentId}.epub`, buffer));
526
+ entryPromises.push(this.putBuffer(`${documentId}.${fileType}`, buffer));
448
527
  // upload metadata
449
528
  const metadata = {
450
529
  type: "DocumentType",
@@ -454,7 +533,16 @@ class Remarkable {
454
533
  synced: true,
455
534
  lastModified,
456
535
  };
457
- entryPromises.push(this.putText(`${documentId}.metadata`, JSON.stringify(metadata)));
536
+ entryPromises.push(this.putMetadata(documentId, metadata));
537
+ entryPromises.push(this.putText(`${documentId}.content`, JSON.stringify(content)));
538
+ // NOTE we technically get the entries a bit earlier, so could upload this
539
+ // before all contents are uploaded, but this also saves us from uploading
540
+ // the contents entry before all have uploaded successfully
541
+ const entries = await Promise.all(entryPromises);
542
+ return await this.putEntries(documentId, entries);
543
+ }
544
+ /** upload an epub */
545
+ async putEpub(visibleName, buffer, { parent = "", margins = 125, orientation, textAlignment, textScale = 1, lineHeight = -1, fontName = "", cover = "visited", lastTool, } = {}) {
458
546
  // upload content file
459
547
  const content = {
460
548
  dummyDocument: false,
@@ -478,12 +566,78 @@ class Remarkable {
478
566
  textAlignment,
479
567
  fontName,
480
568
  };
481
- entryPromises.push(this.putText(`${documentId}.content`, JSON.stringify(content)));
482
- // NOTE we technically get the entries a bit earlier, so could upload this
483
- // before all contents are uploaded, but this also saves us from uploading
484
- // the contents entry before all have uploaded successfully
485
- const entries = await Promise.all(entryPromises);
486
- return await this.putEntries(documentId, entries);
569
+ return await this.#putContent(visibleName, buffer, "epub", parent, content);
570
+ }
571
+ /** upload a pdf */
572
+ async putPdf(visibleName, buffer, { parent = "", orientation, cover = "first", lastTool } = {}) {
573
+ // upload content file
574
+ const content = {
575
+ dummyDocument: false,
576
+ extraMetadata: {
577
+ LastTool: lastTool,
578
+ },
579
+ fileType: "pdf",
580
+ pageCount: 0,
581
+ lastOpenedPage: 0,
582
+ lineHeight: -1,
583
+ margins: 125,
584
+ textScale: 1,
585
+ pages: [],
586
+ coverPageNumber: cover === "first" ? 0 : -1,
587
+ formatVersion: 1,
588
+ orientation,
589
+ };
590
+ return await this.#putContent(visibleName, buffer, "pdf", parent, content);
591
+ }
592
+ /** send sync complete request */
593
+ async syncComplete(generation) {
594
+ // NOTE this is done manually to properly serialize the bigint
595
+ const body = `{ "generation": ${generation} }`;
596
+ await this.#authedFetch(`${this.#syncHost}/sync/v2/sync-complete`, {
597
+ body,
598
+ });
599
+ }
600
+ /** get entries and metadata for all files */
601
+ async getEntriesMetadata() {
602
+ const resp = await this.#authedFetch(`${this.#syncHost}/doc/v2/files`, {
603
+ method: "GET",
604
+ headers: {
605
+ "rm-source": "RoR-Browser",
606
+ },
607
+ });
608
+ const raw = await resp.text();
609
+ const res = JSON.parse(raw);
610
+ const schema = {
611
+ elements: metadataEntrySchema,
612
+ };
613
+ validate(schema, res);
614
+ return res;
615
+ }
616
+ /** upload a file */
617
+ async #uploadFile(visibleName, buffer, contentType) {
618
+ const encoder = new TextEncoder();
619
+ const meta = encoder.encode(JSON.stringify({ file_name: visibleName }));
620
+ const resp = await this.#authedFetch(`${this.#syncHost}/doc/v2/files`, {
621
+ body: buffer,
622
+ headers: {
623
+ "content-type": contentType,
624
+ "rm-meta": fromByteArray(meta),
625
+ "rm-source": "RoR-Browser",
626
+ },
627
+ });
628
+ const raw = await resp.text();
629
+ const res = JSON.parse(raw);
630
+ validate(uploadEntrySchema, res);
631
+ return res;
632
+ }
633
+ /** upload an epub */
634
+ async uploadEpub(visibleName, buffer) {
635
+ return await this.#uploadFile(visibleName, buffer, "application/epub+zip");
636
+ }
637
+ /** upload a pdf */
638
+ async uploadPdf(visibleName, buffer) {
639
+ // TODO why doesn't this work
640
+ return await this.#uploadFile(visibleName, buffer, "application/pdf");
487
641
  }
488
642
  }
489
643
  /**
@@ -492,11 +646,12 @@ class Remarkable {
492
646
  * This gets a temporary authentication token with the device token. If
493
647
  * requests start failing, simply recreate the api instance.
494
648
  *
495
- * @param deviceToken - the device token proving this api instance is registered. Create one with {@link register}.
649
+ * @param deviceToken - the device token proving this api instance is
650
+ * registered. Create one with {@link register}.
496
651
  * @returns an api instance
497
652
  */
498
- export async function remarkable(deviceToken, { fetch = globalThis.fetch, cache, subtle = globalThis.crypto?.subtle, authUrl = AUTH_URL, blobUrl = BLOB_URL, } = {}) {
499
- const resp = await fetch(`${authUrl}/token/json/2/user/new`, {
653
+ export async function remarkable(deviceToken, { fetch = globalThis.fetch, cache, subtle = globalThis.crypto?.subtle, authHost = AUTH_HOST, syncHost = SYNC_HOST, } = {}) {
654
+ const resp = await fetch(`${authHost}/token/json/2/user/new`, {
500
655
  method: "POST",
501
656
  headers: {
502
657
  Authorization: `Bearer ${deviceToken}`,
@@ -506,5 +661,5 @@ export async function remarkable(deviceToken, { fetch = globalThis.fetch, cache,
506
661
  throw new Error(`couldn't fetch auth token: ${resp.statusText}`);
507
662
  }
508
663
  const userToken = await resp.text();
509
- return new Remarkable(userToken, fetch, cache, subtle, blobUrl);
664
+ return new Remarkable(userToken, fetch, cache, subtle, syncHost);
510
665
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rmapi-js",
3
- "version": "1.1.0",
3
+ "version": "2.1.0",
4
4
  "description": "JavaScript implementation of the reMarkable 1.5 api",
5
5
  "repository": "git@github.com:erikbrinkman/rmapi-js.git",
6
6
  "author": "Erik Brinkman <erik.brinkman@gmail.com>",
@@ -8,54 +8,55 @@
8
8
  "keywords": [
9
9
  "remarkable"
10
10
  ],
11
- "types": "dist/index.d.ts",
11
+ "types": "./dist/index.d.ts",
12
12
  "module": "bundle/rmapi-js.esm.min.js",
13
13
  "main": "bundle/rmapi-js.cjs.min.js",
14
14
  "unpkg": "bundle/rmapi-js.iife.min.js",
15
15
  "files": [
16
- "/bundle/*.js",
17
- "/dist/**/*.js",
18
- "/dist/**/*.d.ts"
16
+ "./bundle/*.js",
17
+ "./dist/**/*.js",
18
+ "./dist/**/*.d.ts"
19
19
  ],
20
- "packageManager": "yarn@3.2.4",
20
+ "packageManager": "yarn@3.3.1",
21
21
  "scripts": {
22
22
  "doc": "typedoc",
23
- "fmt": "prettier --cache --write 'src/*.ts' '*.json' 'bundle.mjs'",
24
- "lint": "tsc && eslint --cache 'src/*.ts' 'bundle.mjs' && typedoc --emit none",
23
+ "fmt": "prettier --cache --write 'src/*.ts' '*.json' bundle.mjs",
24
+ "lint": "tsc && eslint --cache 'src/*.ts' bundle.mjs && typedoc --emit none",
25
25
  "test": "jest --coverage",
26
26
  "build": "tsc -p tsconfig.build.json && yarn node bundle.mjs",
27
27
  "prepack": "yarn lint && yarn test && yarn build"
28
28
  },
29
29
  "dependencies": {
30
- "ajv": "^8.11.0",
30
+ "ajv": "^8.11.2",
31
+ "base64-js": "^1.5.1",
32
+ "jtd": "^0.1.1",
31
33
  "uuid": "^9.0.0"
32
34
  },
33
35
  "devDependencies": {
34
- "@babel/core": "^7.19.3",
35
- "@babel/preset-env": "^7.19.3",
36
+ "@babel/core": "^7.20.7",
37
+ "@babel/preset-env": "^7.20.2",
36
38
  "@babel/preset-typescript": "^7.18.6",
37
- "@types/babel__core": "^7.1.19",
39
+ "@types/babel__core": "^7.1.20",
38
40
  "@types/babel__preset-env": "^7.9.2",
39
- "@types/jest": "^29.1.2",
40
- "@types/node": "^18.8.3",
41
- "@types/uuid": "^8.3.4",
42
- "@typescript-eslint/eslint-plugin": "^5.39.0",
43
- "@typescript-eslint/parser": "^5.39.0",
41
+ "@types/jest": "^29.2.4",
42
+ "@types/node": "^18.11.18",
43
+ "@types/uuid": "^9.0.0",
44
+ "@typescript-eslint/eslint-plugin": "^5.47.1",
45
+ "@typescript-eslint/parser": "^5.47.1",
44
46
  "@yarnpkg/esbuild-plugin-pnp": "^3.0.0-rc.15",
45
- "babel-jest": "^29.1.2",
46
- "chalk": "^5.1.0",
47
- "esbuild": "^0.15.10",
48
- "eslint": "^8.24.0",
47
+ "babel-jest": "^29.3.1",
48
+ "chalk": "^5.2.0",
49
+ "esbuild": "^0.16.10",
50
+ "eslint": "^8.30.0",
49
51
  "eslint-config-prettier": "^8.5.0",
50
- "eslint-plugin-jest": "^27.1.1",
51
- "eslint-plugin-spellcheck": "^0.0.19",
52
+ "eslint-plugin-jest": "^27.1.7",
53
+ "eslint-plugin-spellcheck": "^0.0.20",
52
54
  "eslint-plugin-tsdoc": "^0.2.17",
53
- "jest": "^29.1.2",
54
- "jtd": "^0.1.1",
55
- "prettier": "^2.7.1",
56
- "prettier-plugin-organize-imports": "^3.1.1",
57
- "typedoc": "^0.23.15",
58
- "typescript": "^4.8.4"
55
+ "jest": "^29.3.1",
56
+ "prettier": "^2.8.1",
57
+ "prettier-plugin-organize-imports": "^3.2.1",
58
+ "typedoc": "^0.23.23",
59
+ "typescript": "^4.9.4"
59
60
  },
60
61
  "prettier": {
61
62
  "plugins": [
@@ -94,6 +95,7 @@
94
95
  "node": true
95
96
  },
96
97
  "rules": {
98
+ "@typescript-eslint/no-inferrable-types": "off",
97
99
  "tsdoc/syntax": "error",
98
100
  "no-warning-comments": [
99
101
  "error",
@@ -121,6 +123,7 @@
121
123
  "Paintbrushv",
122
124
  "Pencilv",
123
125
  "authed",
126
+ "bigints",
124
127
  "ebooks",
125
128
  "epub",
126
129
  "fineliner",
@@ -128,6 +131,7 @@
128
131
  "iife",
129
132
  "incrementing",
130
133
  "linux",
134
+ "macos",
131
135
  "rmapi",
132
136
  "subfiles",
133
137
  "urls",