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/README.md +14 -0
- package/dist/codes.d.ts +35 -0
- package/dist/codes.js +61 -0
- package/dist/index.d.ts +77 -2
- package/dist/index.js +160 -2
- package/dist/raw.d.ts +15 -1
- package/dist/raw.js +49 -33
- package/dist/rm5.d.ts +6 -30
- package/dist/rm5.js +3 -43
- package/dist/rm6.d.ts +7 -6
- package/dist/rm6.js +4 -3
- package/dist/rmapi-js.esm.min.js +85 -12
- package/package.json +7 -7
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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() }).
|
|
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
|
-
.
|
|
91
|
+
.loose()
|
|
92
92
|
.optional(),
|
|
93
93
|
redir: z
|
|
94
94
|
.object({ timestamp: z.string(), value: z.number().int() })
|
|
95
|
-
.
|
|
95
|
+
.loose()
|
|
96
96
|
.optional(),
|
|
97
97
|
scrollTime: z
|
|
98
98
|
.object({
|
|
99
99
|
timestamp: z.string(),
|
|
100
|
-
value: z.
|
|
100
|
+
value: z.iso.datetime({ offset: true }),
|
|
101
101
|
})
|
|
102
|
-
.
|
|
102
|
+
.loose()
|
|
103
103
|
.optional(),
|
|
104
104
|
verticalScroll: z
|
|
105
105
|
.object({ timestamp: z.string(), value: z.number() })
|
|
106
|
-
.
|
|
106
|
+
.loose()
|
|
107
107
|
.optional(),
|
|
108
108
|
deleted: z
|
|
109
109
|
.object({ timestamp: z.string(), value: z.number().int() })
|
|
110
|
-
.
|
|
110
|
+
.loose()
|
|
111
111
|
.optional(),
|
|
112
112
|
modifed: z.string().optional(),
|
|
113
113
|
})
|
|
114
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
125
|
+
.loose())
|
|
128
126
|
.nullable(),
|
|
129
127
|
})
|
|
130
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
193
|
+
.loose();
|
|
196
194
|
const documentContent = commonDocumentContent
|
|
197
195
|
.extend({ tags: z.array(tag).optional() })
|
|
198
|
-
.
|
|
196
|
+
.loose();
|
|
199
197
|
const legacyDocumentContent = commonDocumentContent
|
|
200
198
|
.extend({ tags: z.array(z.string()).optional() })
|
|
201
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
243
|
+
.loose()),
|
|
246
244
|
})
|
|
247
|
-
.
|
|
245
|
+
.loose())),
|
|
248
246
|
})
|
|
249
|
-
.
|
|
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() }).
|
|
251
|
+
layers: z.array(z.object({ name: z.string() }).loose()),
|
|
254
252
|
})
|
|
255
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
.
|
|
290
|
+
.loose();
|
|
293
291
|
const nativeItemRef = z
|
|
294
292
|
.object({
|
|
295
293
|
docID: z.string(),
|
|
296
294
|
hash: z.string(),
|
|
297
295
|
})
|
|
298
|
-
.
|
|
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
|
|
44
|
-
brushType:
|
|
45
|
-
/** the
|
|
46
|
-
color:
|
|
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
|
-
|
|
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
|
|
58
|
-
tool:
|
|
59
|
-
/** the
|
|
60
|
-
color:
|
|
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
|
|
98
|
-
color:
|
|
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();
|