UNPKG

a-remarkable-js-sdk

Version:

a reMarkable Cloud API wrapper written in TypeScript

546 lines (527 loc) 22.2 kB
type DeviceDescription = 'browser-chrome' | 'desktop-macos' | 'desktop-windows' | 'mobile-android' | 'mobile-ios' | 'remarkable'; /** * Represents a reMarkable Cloud API session. * * The reMarkable Cloud API uses JWT token based authentication. All requests * performed to the API (expect the authentication ones) must contain a valid * JWT token which identifies the (@link Device) performing the request. * * The `Session` class provides an interface to access the authentication * token information and verify its validity. */ declare class Session { readonly deviceId: string; readonly expiresAt: Date; readonly token: string; constructor(sessionToken: string); get expired(): boolean; } /** * Represents a reMarkable device. Provides an interface to * authenticate with the reMarkable Cloud API. * * The reMarkable Cloud API uses `devices` to authenticate users. A * `device` is any software paired to a reMarkable Cloud user account. * `devices` are represented by a `device token`. A `device token` is * a JWT token without expiration date. It is used to fetch `session tokens` * from the reMarkable Cloud API, which can be used to perform authenticated * request to the different API services. * * The `Device` class encapsulates the logic to pair new applications to * reMarkable Cloud user accounts and create user sessions for interacting * with the reMarkable Cloud API. * * Paired `Devices` are listed in: [https://my.remarkable.com/device/remarkable](https://my.remarkable.com/device/remarkable) */ declare class Device { /** * Creates a new device and pairs it to a reMarkable Cloud user account. * * @param id - Unique `device` ID (uuid v4) * @param description - Label which indicates the `device` running environment (web browser, mobile app, ...) * @param oneTimeCode - One-time password to authenticate reMarkable Cloud user account when pairing `device` * @param HttpClientConstructor - HttpClient used for generating pair token */ static pair(id: string, description: DeviceDescription, oneTimeCode: string, HttpClientConstructor?: unknown): Promise<Device>; /** * `device` unique identifier (uuid v4) */ readonly id: string; /** * `device` running environment (web browser, mobile app, ...) */ readonly description: DeviceDescription; /** * reMarkable Cloud API token associated to `device` */ readonly token: string; private readonly httpClient; constructor(deviceToken: string, HttpClientConstructor?: unknown); /** * Creates a new `device` `session`. * * Fetches a new session token from the reMarkable Cloud API. This token * can be used to perform authenticated requests to the reMarkable Cloud API * in behalf of the user account associated to the `device`. */ connect(): Promise<Session>; } /** * Represents a reMarkable Cloud folder */ declare class Folder { id: string; hash: string; name: string; folders?: Folder[]; documents?: Document[]; parentFolder?: Folder; lastModified?: Date; constructor(id: string, hash: string, name: string, lastModified: Date, parentFolder?: Folder, folders?: Folder[], documents?: Document[]); get root(): boolean; } /** * Represents a reMarkable Cloud document */ declare class Document { id: string; hash: string; name: string; fileType: 'pdf' | 'epub' | 'notebook'; folder?: Folder; lastModified: Date; lastOpened: Date; constructor(id: string, hash: string, name: string, fileType: 'pdf' | 'epub' | 'notebook', lastModified?: Date, lastOpened?: Date, folder?: Folder); } /** * The reMarkable API only works with `.pdf` and `.epub` files. This error is * raised when an `Buffer` with an unsupported file extension is passed to * the `FileBufferType` class. */ declare class UnsupportedFileExtensionError extends Error { } /** * Represents the type of the file associated to its buffer. * * It is possible to identify the type of a file by examining its buffer. * This class encapsulates the logic to infer a files type from its * respective buffer. * * Since the reMarkable Cloud API only allows uploading `.pdf` and `.epub` * files, the class only handles buffers with that extension, raising an * { @link UnsupportedFileExtensionError } when a buffer from an unsupported * file extension is passed. */ declare class FileBufferType { static extension(buffer: Buffer): 'pdf' | 'epub'; static mimeType(buffer: Buffer): string; /** * Buffer file extension */ readonly extension: string; /** * MIME type of the buffer file */ readonly mimeType: string; constructor(buffer: Buffer); } type HeadersPayload = Record<string, string>; /** * { @link HttpClient } { @link Request } headers * * Encapsulates a collection of `http` `header` key - value pairs * and provides logic to export them in different formats * compatible with `http` libraries. */ declare class Headers { #private; constructor(headers: HeadersPayload); get entries(): Record<string, string>; } /** * { @link HttpClient } { @link Request } context * * `HttpClient` instance configuration. Given an `HttpClient` with a * `Context`, the `Context` represents the base URL and headers set * used in all http requests performed with the client. */ declare class Context { readonly host: string; readonly headers: Headers; constructor(host: string, headers?: HeadersPayload); } /** * HTTP request `body` types supported by { @link HttpClient } { @link Body }. * * These types can be converted into a `serialized` format, compatible with `http` libraries, * by the { @link HttpClient } { @link Body } to be dispatched as `body` in an * HTTP POST / PATCH / PUT request. */ type BodyPayload = Record<string, string | boolean | number> | ArrayBuffer | Buffer | string; /** * Interface for library HTTP client. * * Provides a RESTful interface for performing HTTP requests. All http * requests performed by the library should be done through this interface. * * `HttpClients` interface consists on a set of static methods for performing * HTTP requests. To perform multiple requests with a base `host` and set of * `headers` use the instance methods by instantiating the client with the * base `host` and `headers` as context. */ declare abstract class HttpClient { static get(host: string, path: string, headers?: HeadersPayload): Promise<Response>; static post(host: string, path: string, headers?: HeadersPayload, body?: BodyPayload): Promise<Response>; static patch(host: string, path: string, headers?: HeadersPayload, body?: BodyPayload): Promise<Response>; static put(host: string, path: string, headers?: HeadersPayload, body?: BodyPayload): Promise<Response>; static delete(host: string, path: string, headers?: HeadersPayload): Promise<Response>; readonly context: Context; constructor(host: string, headers?: HeadersPayload); /** * Perform a GET request. * * @param path - Request path destination (https://{@link host}/{@link path}) * @param headers - Request headers. Merged with client { @link context.headers } when performing request */ get(path: string, headers?: HeadersPayload): Promise<Response>; /** * Perform a POST request. * * @param path - Request path destination (https://{@link host}/{@link path}) * @param body - Request body payload * @param headers - Request headers. Merged with client { @link context.headers } when performing request */ post(path: string, body?: BodyPayload, headers?: HeadersPayload): Promise<Response>; /** * Perform a PATCH request. * * @param path - Request path destination (https://{@link host}/{@link path}) * @param body - Request body payload * @param headers - Request headers. Merged with client { @link context.headers } when performing request */ patch(path: string, body?: BodyPayload, headers?: HeadersPayload): Promise<Response>; /** * Perform a PUT request. * * @param path - Request path destination (https://{@link host}/{@link path}) * @param body - Request body payload * @param headers - Request headers. Merged with client { @link context.headers } when performing request */ put(path: string, body?: BodyPayload, headers?: HeadersPayload): Promise<Response>; /** * Perform a DELETE request. * * @param path - Request path destination (https://{@link host}/{@link path}) * @param headers - Request headers. Merged with client { @link context.headers } when performing request */ delete(path: string, headers?: HeadersPayload): Promise<Response>; /** * Get the underlying `HttpClient` instance class reference. * * Used to perform HTTP requests through the client specific http static methods. * * @private */ private classReference; /** * Request specific configuration. Merges client { @link context.headers } * with request specific headers. * * @param headers - Request specific headers * * @private */ private requestContext; } /** * Generates `HttpClient` instances preconfigured for performing * requests to the different services available in the reMarkable * Cloud API. * * Each service in the reMarkable Cloud API (such as authentication, * document storage, ...) has a specific host. The `/service` API * endpoint allows developers to fetch the these hosts. This class * provides a public interface of methods representing all the * services available in the API. * * Use these methods to get instances of the `HttpClient` configured * with the corresponding host and authentication headers for the * service you want to make use of. */ declare class ServiceManager { /** * Returns `HttpClient` configured with host and header options to * perform requests to the reMarkable production API. * * This API endpoint is not a service per-se, but provides some similar * utilities other services provide (such as device pairing or session * creation). To keep the logic to fetch endpoints consistent, this * method encapsulates the endpoint logic as if it was another service. * * Some of the utilities the endpoint provide does not require from * any headers. By defining this method as static, we can make use * of this endpoint without requiring a `Device` instance (as it * is required for other endpoints). */ static productionHttpClient(headers?: HeadersPayload, HttpClientConstructor?: unknown): HttpClient; readonly session: Session; readonly httpClient: HttpClient; constructor(session: Session, HttpClientConstructor?: unknown); /** * Returns `HttpClient` configured with host and header options to * perform requests to the reMarkable Document Storage API service. */ documentStorageHttpClient(): Promise<HttpClient>; /** * Returns `HttpClient` configured with host and header options to * perform requests to the reMarkable internal API. * * This API endpoint is not a service per-se, but provides some similar * utilities other services provide (such as file upload). To keep the * logic to fetch endpoints consistent, this method encapsulates the * endpoint logic as if it was another service. */ internalCloudHttpClient(): Promise<HttpClient>; private serviceHttpClient; } declare class FileNotUploadedError extends Error { } /** * Represents a reference to an uploaded { @link Document } * * In the reMarkable Cloud API, documents are identified by a unique * ID and Hash. While the ID is always the same, the hash changes * conforming changes are pushed to the user account file system. * * The { @link DocumentReference } class parses the reMarkable Cloud * API success upload response and returns a reference to the uploaded * document. */ declare class DocumentReference { readonly id: string; readonly hash: string; constructor(id: string, hash: string); } /** * Represents a file content, encoded as a buffer, ready to be uploaded * to reMarkable Cloud. * * Encapsulates the logic to handle file upload. Given the Buffer * content of a file, it verifies the type compatibility with the * reMarkable Cloud API and provides an interface to upload the file * and get a reference to its cloud equivalent when successfully * pushed. */ declare class FileBuffer { /** * Creates a FileBuffer from a local file * * Used to easily upload local files to reMarkable cloud * * @param {string} path * @param {ServiceManager} serviceManager */ static fromLocalFile(path: string, serviceManager: ServiceManager): Promise<FileBuffer>; /** * Creates FileBuffer from file Buffer and uploads it * * @param name - File name * @param buffer - File content * @param serviceManager */ static upload(name: string, buffer: Buffer, serviceManager: ServiceManager): Promise<FileBuffer>; readonly name: string; readonly buffer: Buffer; readonly type: FileBufferType; documentReference?: DocumentReference; private httpClient?; private readonly serviceManager; constructor(name: string, buffer: Buffer, serviceManager: ServiceManager); get uploaded(): boolean; upload(): Promise<DocumentReference>; private internalCloudHttpClient; private get encodedName(); } /** * Represents a snapshot of the reMarkable Cloud API file system. * * Provides a list of all { @link Document }s and { @link Folder }s in * the user's reMarkable Cloud account in a specific point in time. * * A snapshot is valid as long as the user does not perform any write * operations on the reMarkable Cloud file system. As soon a file in * the system is uploaded or modified, all the files hashes are modify, * causing the snapshot to be de-synchornized with the actual reMarkable * Cloud file system. * * You can see the snapshot as a cache of the reMarkable Cloud file system. */ declare class FileSystemSnapshot { #private; constructor(documents: Document[], folders: Folder[]); get documents(): Document[]; get folders(): Folder[]; get rootFolder(): Folder; document(id: string): Document | undefined; folder(id: string): Folder | undefined; } /** * Represents the reMarkable Cloud API file system. Provides an * interface to retrieve the list of {@link Document}s and {@link Folder}s * in a user reMarkable Cloud account and navigate through them. * * The reMarkable API `/doc/v2/files` endpoint returns a list of all files * in a user reMarkable Cloud account with their respective metadata. * * The `FileSystem` class parses the API response and maps the file system * hierarchy to a set of {@link Document} and {@link Folder} instances, * providing a virtual recreation of the actual file system tree, which * can be then used to navigate through it in the similar way as in any * other file system. */ declare class FileSystem { #private; constructor(serviceManager: ServiceManager); get lastFetchSnapshot(): FileSystemSnapshot | undefined; /** * Requests new reMarkable Cloud snapshot of all documents and folders in user account. */ snapshot(): Promise<FileSystemSnapshot>; /** * Requests new reMarkable Cloud list of all documents in user account. * * Consider using {@link snapshot} method to get a snapshot of the entire file system * and fetch the documents from there instead of calling this method. Each document * request triggers a fetch call of the whole file system and the post-processing * of the response to build the file system tree. This is an expensive operation * that should be only invoked when changes have been made to the file system * (files uploaded, updated, deleted, etc). * * If no changes were uploaded to the reMarkable Cloud you should use the {@link snapshot} * method to get the list of documents and folders in the user account. The snapshot works * as a cache, and contains the file system information of the last request made to the * reMarkable Cloud API. */ document(id: string): Promise<Document | undefined>; /** * Requests new reMarkable Cloud list of all folder in user account. * * Consider using {@link snapshot} method to get a snapshot of the entire file system * and fetch the folder from there instead of calling this method. Each folder * request triggers a fetch call of the whole file system and the post-processing * of the response to build the file system tree. This is an expensive operation * that should be only invoked when changes have been made to the file system * (files uploaded, updated, deleted, etc). * * If no changes were uploaded to the reMarkable Cloud you should use the {@link snapshot} * method to get the list of documents and folders in the user account. The snapshot works * as a cache, and contains the file system information of the last request made to the * reMarkable Cloud API. */ folder(id: string): Promise<Folder | undefined>; private fetchSnapshot; } /** * reMarkable API `/downloads` endpoint response payload * * Represents the URL to access the content of a `hash`. * * Download URLs are signed. The `hash` content is accessible * through an HTTP request to its `url` with the `method` specified * (no authentication required). * * Once the `expires` date is reached, the URL is no longer valid. */ interface HashPathPayload { expires: string; method: 'GET' | 'PUT' | 'PATCH'; relative_path: string; url: string; } /** * Represents the URL to access the content of a `hash`. * * A `hash` is a string which uniquely identifies a piece of information in the * reMarkable cloud. A piece of information can be a `Document`, a `Folder`, * metadata associated to a `Document` or a `Folder`, etc. * * To access the content of a `hash`, the reMarkable API provides a `/downloads` * endpoint. This endpoint returns the download URL for a given `hash`. Download * URLs are signed URLs with an expiration date. When performing an HTTP request * to the download URL, the content of the `hash` is returned. Once the expiration * date is reached, the URL is no longer valid, requiring a new request to the * `/downloads` endpoint to get a new download URL. * * The `HashUrl` class provides an interface to download the content of a `hash`. */ declare class HashUrl { /** * Returns `HashUrl` for a given `hash`. * * @param {string} hash * @param {ServiceManager} serviceManager */ static fromHash(hash: string, serviceManager: ServiceManager): Promise<HashUrl>; /** * Returns `HashUrl` for the `hash` associated to the root folder. * * The root folder represents the root path of the reMarkable cloud storage. * * @param {ServiceManager} serviceManager */ static fromRootHash(serviceManager: ServiceManager): Promise<HashUrl>; expires: Date; method: 'GET' | 'PUT' | 'PATCH'; relativePath: string; url: URL; constructor(hashPathPayload: HashPathPayload); get expired(): boolean; fetch(): Promise<Response>; } /** * { @link HttpClient } which uses web browser `fetch` method to perform HTTP requests. */ declare class FetchClient extends HttpClient { static get(host: string, path: string, headers?: HeadersPayload): Promise<Response>; static post(host: string, path: string, headers?: HeadersPayload, body?: BodyPayload | null): Promise<Response>; static patch(host: string, path: string, headers?: HeadersPayload, body?: BodyPayload | null): Promise<Response>; static put(host: string, path: string, headers?: HeadersPayload, body?: BodyPayload | null): Promise<Response>; static delete(host: string, path: string, headers?: HeadersPayload): Promise<Response>; private static makeRequest; private static request; } /** * { @link HttpClient } which uses Node.js `https` module to perform HTTP requests. */ declare class NodeClient extends HttpClient { static get(host: string, path: string, headers?: HeadersPayload): Promise<Response>; static post(host: string, path: string, headers?: HeadersPayload, body?: BodyPayload | null): Promise<Response>; static patch(host: string, path: string, headers?: HeadersPayload, body?: BodyPayload | null): Promise<Response>; static put(host: string, path: string, headers?: HeadersPayload, body?: BodyPayload | null): Promise<Response>; static delete(host: string, path: string, headers?: HeadersPayload): Promise<Response>; private static makeRequest; private static request; } /** * reMarkable Cloud API client. * * Provides an interface to interact with the reMarkable Cloud API: * - Navigate through the file system * - Upload documents */ declare class RemarkableClient { #private; static withFetchHttpClient(deviceToken?: string, sessionToken?: string): RemarkableClient; static withNodeHttpClient(deviceToken?: string, sessionToken?: string): RemarkableClient; constructor(deviceToken?: string, sessionToken?: string, httpClientConstructor?: unknown); get device(): Device; get session(): Session; pair(id: string, description: DeviceDescription, oneTimeCode: string): Promise<boolean>; connect(): Promise<void>; document(id: string): Promise<Document | undefined>; folder(id: string): Promise<Folder | undefined>; upload(name: string, buffer: Buffer): Promise<DocumentReference>; get sessionExpired(): boolean; get paired(): boolean; } export { Device, type DeviceDescription, Document, DocumentReference, FetchClient, FileBuffer, FileBufferType, FileNotUploadedError, FileSystem, Folder, HashUrl, HttpClient, NodeClient, RemarkableClient, ServiceManager, Session, UnsupportedFileExtensionError };