rmapi-js 13.0.0 → 14.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/raw.js CHANGED
@@ -66,14 +66,14 @@ const tag = z
66
66
  name: z.string(),
67
67
  timestamp: z.number(),
68
68
  })
69
- .passthrough();
69
+ .loose();
70
70
  const pageTag = z
71
71
  .object({
72
72
  name: z.string(),
73
73
  pageId: z.string(),
74
74
  timestamp: z.number(),
75
75
  })
76
- .passthrough();
76
+ .loose();
77
77
  const documentMetadata = z
78
78
  .object({
79
79
  authors: z.array(z.string()).optional(),
@@ -81,53 +81,51 @@ const documentMetadata = z
81
81
  publicationDate: z.string().optional(),
82
82
  publisher: z.string().optional(),
83
83
  })
84
- .passthrough();
84
+ .loose();
85
85
  const cPagePage = z
86
86
  .object({
87
87
  id: z.string(),
88
- idx: z.object({ timestamp: z.string(), value: z.string() }).passthrough(),
88
+ idx: z.object({ timestamp: z.string(), value: z.string() }).loose(),
89
89
  template: z
90
90
  .object({ timestamp: z.string(), value: z.string() })
91
- .passthrough()
91
+ .loose()
92
92
  .optional(),
93
93
  redir: z
94
94
  .object({ timestamp: z.string(), value: z.number().int() })
95
- .passthrough()
95
+ .loose()
96
96
  .optional(),
97
97
  scrollTime: z
98
98
  .object({
99
99
  timestamp: z.string(),
100
- value: z.string().datetime({ offset: true }),
100
+ value: z.iso.datetime({ offset: true }),
101
101
  })
102
- .passthrough()
102
+ .loose()
103
103
  .optional(),
104
104
  verticalScroll: z
105
105
  .object({ timestamp: z.string(), value: z.number() })
106
- .passthrough()
106
+ .loose()
107
107
  .optional(),
108
108
  deleted: z
109
109
  .object({ timestamp: z.string(), value: z.number().int() })
110
- .passthrough()
110
+ .loose()
111
111
  .optional(),
112
112
  modifed: z.string().optional(),
113
113
  })
114
- .passthrough();
114
+ .loose();
115
115
  const cPages = z
116
116
  .object({
117
- lastOpened: z
118
- .object({ timestamp: z.string(), value: z.string() })
119
- .passthrough(),
117
+ lastOpened: z.object({ timestamp: z.string(), value: z.string() }).loose(),
120
118
  original: z
121
119
  .object({ timestamp: z.string(), value: z.number().int() })
122
- .passthrough(),
120
+ .loose(),
123
121
  pages: z.array(cPagePage),
124
122
  uuids: z
125
123
  .array(z
126
124
  .object({ first: z.string(), second: z.number().int().nonnegative() })
127
- .passthrough())
125
+ .loose())
128
126
  .nullable(),
129
127
  })
130
- .passthrough();
128
+ .loose();
131
129
  const collectionContent = z
132
130
  .object({
133
131
  tags: z.array(tag).optional(),
@@ -162,7 +160,7 @@ const documentContentOptional = {
162
160
  formatVersion: z.number().int().nonnegative().optional(),
163
161
  keyboardMetadata: z
164
162
  .object({ count: z.number().int().nonnegative(), timestamp: z.number() })
165
- .passthrough()
163
+ .loose()
166
164
  .optional(),
167
165
  lastOpenedPage: z.number().int().optional(),
168
166
  margins: z.number().int().nonnegative().optional(),
@@ -183,7 +181,7 @@ const documentContentOptional = {
183
181
  m32: z.number().optional(),
184
182
  m33: z.number().optional(),
185
183
  })
186
- .passthrough()
184
+ .loose()
187
185
  .optional(),
188
186
  viewBackgroundFilter: z.enum(["off", "fullpage"]).optional(),
189
187
  zoomMode: z
@@ -192,13 +190,13 @@ const documentContentOptional = {
192
190
  };
193
191
  const commonDocumentContent = z
194
192
  .object({ ...documentContentRequired, ...documentContentOptional })
195
- .passthrough();
193
+ .loose();
196
194
  const documentContent = commonDocumentContent
197
195
  .extend({ tags: z.array(tag).optional() })
198
- .passthrough();
196
+ .loose();
199
197
  const legacyDocumentContent = commonDocumentContent
200
198
  .extend({ tags: z.array(z.string()).optional() })
201
- .passthrough();
199
+ .loose();
202
200
  const templateContent = z
203
201
  .object({
204
202
  id: z.string().optional(),
@@ -216,7 +214,7 @@ const templateContent = z
216
214
  items: z.array(z.unknown()),
217
215
  formatVersion: z.number().int().nonnegative().optional(),
218
216
  })
219
- .passthrough();
217
+ .loose();
220
218
  // content payloads aren't discriminable (legacy/modern differ only by tags
221
219
  // element type), so this is an ordered union: the first matching variant wins
222
220
  const content = z.union([
@@ -242,17 +240,17 @@ const highlightsFile = z
242
240
  width: z.number(),
243
241
  height: z.number(),
244
242
  })
245
- .passthrough()),
243
+ .loose()),
246
244
  })
247
- .passthrough())),
245
+ .loose())),
248
246
  })
249
- .passthrough();
247
+ .loose();
250
248
  const pageMetadataReg = /\/[^/]+-metadata\.json$/;
251
249
  const pageMetadata = z
252
250
  .object({
253
- layers: z.array(z.object({ name: z.string() }).passthrough()),
251
+ layers: z.array(z.object({ name: z.string() }).loose()),
254
252
  })
255
- .passthrough();
253
+ .loose();
256
254
  const metadata = z
257
255
  .object({
258
256
  lastModified: z.string().optional(),
@@ -271,7 +269,7 @@ const metadata = z
271
269
  new: z.boolean().optional(),
272
270
  source: z.string().optional(),
273
271
  })
274
- .passthrough();
272
+ .loose();
275
273
  /** parse and validate the json text of a `.metadata` file */
276
274
  export function parseMetadata(text) {
277
275
  const loaded = JSON.parse(text);
@@ -282,20 +280,20 @@ const updatedRootHash = z
282
280
  hash: z.string(),
283
281
  generation: z.number(),
284
282
  })
285
- .passthrough();
283
+ .loose();
286
284
  const rootHash = z
287
285
  .object({
288
286
  hash: z.string(),
289
287
  generation: z.number(),
290
288
  schemaVersion: z.number().int().nonnegative(),
291
289
  })
292
- .passthrough();
290
+ .loose();
293
291
  const nativeItemRef = z
294
292
  .object({
295
293
  docID: z.string(),
296
294
  hash: z.string(),
297
295
  })
298
- .passthrough();
296
+ .loose();
299
297
  async function digest(buff) {
300
298
  const digest = await crypto.subtle.digest("SHA-256",
301
299
  // NOTE this is type hinted wrong, but it does work correctly on a uint8 view
@@ -389,6 +387,7 @@ function parseRawEntryLine(line) {
389
387
  */
390
388
  export class RawRemarkable {
391
389
  #authedFetch;
390
+ #authedSocket;
392
391
  #rawHost;
393
392
  #uploadHost;
394
393
  /**
@@ -402,8 +401,9 @@ export class RawRemarkable {
402
401
  */
403
402
  #cache;
404
403
  #maxCachedBytes;
405
- constructor(authedFetch, cache, rawHost, uploadHost, maxCachedBytes) {
404
+ constructor(authedFetch, authedSocket, cache, rawHost, uploadHost, maxCachedBytes) {
406
405
  this.#authedFetch = authedFetch;
406
+ this.#authedSocket = authedSocket;
407
407
  this.#cache = cache;
408
408
  this.#rawHost = rawHost;
409
409
  this.#uploadHost = uploadHost;
@@ -867,6 +867,22 @@ export class RawRemarkable {
867
867
  const { docID, hash } = nativeItemRef.parse(loaded);
868
868
  return { id: docID, hash };
869
869
  }
870
+ /**
871
+ * open the websocket reMarkable pushes notifications to
872
+ *
873
+ * The server drops the socket every few minutes, so anything long lived has
874
+ * to reopen it. {@link RemarkableApi.listen | `listen`} does that, and parses
875
+ * the sync notifications out of what arrives.
876
+ *
877
+ * reMarkable authorizes the handshake with a header, which node and bun can
878
+ * attach but a browser's `WebSocket` can't, so this is server side only.
879
+ *
880
+ * @returns the connecting socket
881
+ */
882
+ notifications() {
883
+ const host = this.#rawHost.replace(/^http/, "ws");
884
+ return this.#authedSocket(`${host}/notifications/ws/json/1`);
885
+ }
870
886
  /**
871
887
  * dump the current cache to a string to preserve between session
872
888
  *
package/dist/rm5.d.ts CHANGED
@@ -13,6 +13,7 @@
13
13
  *
14
14
  * @packageDocumentation
15
15
  */
16
+ import { type RmBrushCode, type RmColorCode } from "./codes.js";
16
17
  /** the flat reMarkable `.lines` file versions read into an {@link RmPageV5} */
17
18
  export type RmVersion = 3 | 5;
18
19
  /** a single sampled point along a stroke */
@@ -30,20 +31,12 @@ export interface RmPoint {
30
31
  /** the pen pressure, from 0 to 1 */
31
32
  pressure: number;
32
33
  }
33
- /**
34
- * a single stroke (called a "line") within a layer
35
- *
36
- * `brushType` and `color` are kept as raw integers because both are overloaded
37
- * across firmware versions (there are two pen-code families, and the color
38
- * field is reinterpreted as a palette index when colored annotations are
39
- * enabled), so the right meaning can only be resolved with the source firmware
40
- * in mind.
41
- */
34
+ /** a single stroke (called a "line") within a layer */
42
35
  export interface RmLine {
43
- /** the raw brush/pen type code */
44
- brushType: number;
45
- /** the raw color code */
46
- color: number;
36
+ /** the pen code, named by {@link rmBrushes | `rmBrushes`} */
37
+ brushType: RmBrushCode;
38
+ /** the color code, named by {@link rmColors | `rmColors`} */
39
+ color: RmColorCode;
47
40
  /** a per-stroke padding field, typically 0 */
48
41
  padding?: number;
49
42
  /** the base brush size */
@@ -70,23 +63,6 @@ export interface RmPageV5 {
70
63
  /** the drawing layers, back to front */
71
64
  layers: RmLayer[];
72
65
  }
73
- /** a decoded pen/tool for a stroke */
74
- export type RmBrush = "brush" | "pencil" | "ballpoint" | "marker" | "fineliner" | "highlighter" | "eraser" | "mechanicalPencil" | "eraseArea" | "calligraphy" | "shader";
75
- /**
76
- * decode a raw {@link RmLine.brushType | `brushType`} code to a pen name
77
- *
78
- * Best-effort and advisory: the reMarkable pen codes come in two firmware
79
- * families and new ones appear over time, so an unrecognized code returns
80
- * `undefined`. The raw `brushType` int stays authoritative.
81
- */
82
- export declare function decodeBrush(code: number): RmBrush | undefined;
83
- /**
84
- * the default (monochrome) reMarkable palette, by raw {@link RmLine.color | `color`}
85
- *
86
- * Advisory only: when "colored annotations" are enabled or on version 6, the
87
- * `color` field is reinterpreted, so resolve against the source firmware.
88
- */
89
- export declare const rmColors: Readonly<Record<number, string>>;
90
66
  /** the length of the fixed `.lines` header, in bytes */
91
67
  export declare const HEADER_LENGTH = 43;
92
68
  /** the ascii prefix of the header, immediately followed by the version digit */
package/dist/rm5.js CHANGED
@@ -13,47 +13,7 @@
13
13
  *
14
14
  * @packageDocumentation
15
15
  */
16
- const BRUSHES = {
17
- 0: "brush",
18
- 12: "brush",
19
- 1: "pencil",
20
- 14: "pencil",
21
- 2: "ballpoint",
22
- 15: "ballpoint",
23
- 3: "marker",
24
- 16: "marker",
25
- 4: "fineliner",
26
- 17: "fineliner",
27
- 5: "highlighter",
28
- 18: "highlighter",
29
- 6: "eraser",
30
- 7: "mechanicalPencil",
31
- 13: "mechanicalPencil",
32
- 8: "eraseArea",
33
- 21: "calligraphy",
34
- 23: "shader",
35
- };
36
- /**
37
- * decode a raw {@link RmLine.brushType | `brushType`} code to a pen name
38
- *
39
- * Best-effort and advisory: the reMarkable pen codes come in two firmware
40
- * families and new ones appear over time, so an unrecognized code returns
41
- * `undefined`. The raw `brushType` int stays authoritative.
42
- */
43
- export function decodeBrush(code) {
44
- return BRUSHES[code];
45
- }
46
- /**
47
- * the default (monochrome) reMarkable palette, by raw {@link RmLine.color | `color`}
48
- *
49
- * Advisory only: when "colored annotations" are enabled or on version 6, the
50
- * `color` field is reinterpreted, so resolve against the source firmware.
51
- */
52
- export const rmColors = {
53
- 0: "black",
54
- 1: "gray",
55
- 2: "white",
56
- };
16
+ import { rmBrushCode, rmColorCode, } from "./codes.js";
57
17
  /** the length of the fixed `.lines` header, in bytes */
58
18
  export const HEADER_LENGTH = 43;
59
19
  /** the ascii prefix of the header, immediately followed by the version digit */
@@ -83,8 +43,8 @@ export function parseV5(data, version) {
83
43
  const numLines = readInt();
84
44
  const lines = [];
85
45
  for (let line = 0; line < numLines; line++) {
86
- const brushType = readInt();
87
- const color = readInt();
46
+ const brushType = rmBrushCode.parse(readInt());
47
+ const color = rmColorCode.parse(readInt());
88
48
  const padding = readInt();
89
49
  const brushBaseSize = readFloat();
90
50
  const unknown = version === 5 ? readInt() : undefined;
package/dist/rm6.d.ts CHANGED
@@ -11,6 +11,7 @@
11
11
  *
12
12
  * @packageDocumentation
13
13
  */
14
+ import { type RmBrushCode, type RmColorCode } from "./codes.js";
14
15
  /**
15
16
  * a CRDT identifier: an `(authorId, counter)` pair, unique across replicas
16
17
  *
@@ -54,10 +55,10 @@ export interface RmV6Point {
54
55
  }
55
56
  /** a version 6 stroke */
56
57
  export interface RmV6Line {
57
- /** the raw pen/tool code */
58
- tool: number;
59
- /** the raw color code */
60
- color: number;
58
+ /** the pen code, named by `rmBrushes` */
59
+ tool: RmBrushCode;
60
+ /** the color code, named by `rmColors` */
61
+ color: RmColorCode;
61
62
  /** the stroke thickness scale */
62
63
  thicknessScale: number;
63
64
  /** the length at which the stroke starts */
@@ -94,8 +95,8 @@ export interface GlyphRange {
94
95
  start?: number;
95
96
  /** the length of the range */
96
97
  length: number;
97
- /** the raw color code */
98
- color: number;
98
+ /** the color code, named by `rmColors` */
99
+ color: RmColorCode;
99
100
  /** the highlighted text */
100
101
  text: string;
101
102
  /** the bounding rectangles of the highlight */
package/dist/rm6.js CHANGED
@@ -11,6 +11,7 @@
11
11
  *
12
12
  * @packageDocumentation
13
13
  */
14
+ import { rmBrushCode, rmColorCode, } from "./codes.js";
14
15
  /** the scene-tree root node id */
15
16
  export const ROOT_ID = { authorId: 0, counter: 1 };
16
17
  /** the CRDT sequence end marker / unset id */
@@ -342,8 +343,8 @@ function readItemEnvelope(reader, readValue) {
342
343
  return { parentId, item: { itemId, leftId, rightId, deletedLength, value } };
343
344
  }
344
345
  function readLineValue(reader, version) {
345
- const tool = reader.readInt(1);
346
- const color = reader.readInt(2);
346
+ const tool = rmBrushCode.parse(reader.readInt(1));
347
+ const color = rmColorCode.parse(reader.readInt(2));
347
348
  const thicknessScale = reader.readDouble(3);
348
349
  const startingLength = reader.readFloat(4);
349
350
  const points = reader.subblock(5, () => {
@@ -394,7 +395,7 @@ function readGlyphValue(reader) {
394
395
  const explicitLength = reader.hasTag(3, TAG_BYTE4)
395
396
  ? reader.readInt(3)
396
397
  : undefined;
397
- const color = reader.readInt(4);
398
+ const color = rmColorCode.parse(reader.readInt(4));
398
399
  const text = reader.readString(5);
399
400
  const rectangles = reader.subblock(6, () => {
400
401
  const count = reader.varuint();