instapaper-ts
Version:
A type-safe API client for Instapaper.
115 lines (114 loc) • 3.94 kB
JavaScript
// src/index.ts
import assert from "assert";
import crypto from "crypto";
import OAuth from "oauth-1.0a";
var Instapaper = class _Instapaper {
baseUrl = "https://www.instapaper.com/api";
oauth;
token;
constructor({
consumerKey,
consumerSecret,
token
}) {
this.oauth = new OAuth({
consumer: { key: consumerKey, secret: consumerSecret },
signature_method: "HMAC-SHA1",
hash_function: (base_string, key) => crypto.createHmac("sha1", key).update(base_string).digest("base64")
});
this.token = token;
}
static async fetchToken({
consumerKey,
consumerSecret,
username,
password
}) {
const oauth = new OAuth({
consumer: { key: consumerKey, secret: consumerSecret },
signature_method: "HMAC-SHA1",
hash_function: (base_string, key2) => crypto.createHmac("sha1", key2).update(base_string).digest("base64")
});
const responseText = await _Instapaper.postForm(
oauth,
"https://www.instapaper.com/api/1/oauth/access_token",
{
x_auth_username: username,
x_auth_password: password,
x_auth_mode: "client_auth"
}
);
const data = new URLSearchParams(responseText);
const key = data.get("oauth_token");
const secret = data.get("oauth_token_secret");
assert(key && secret, "Token exchange failed: key or secret is null.");
return { key, secret };
}
static async postForm(oauth, url, params = {}, token) {
const form = new URLSearchParams();
for (const [key, val] of Object.entries(params)) {
form.append(key, String(val));
}
const headers = oauth.toHeader(
oauth.authorize({ url, method: "POST", data: params }, token)
);
const response = await fetch(url, {
method: "POST",
headers: new Headers({ ...headers }),
body: form
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API error: ${response.status} ${errorText}`);
}
const contentType = response.headers.get("content-type") ?? "";
return contentType.includes("application/json") ? response.json() : response.text();
}
request = (endpoint, params = {}) => _Instapaper.postForm(
this.oauth,
this.baseUrl + endpoint,
params,
this.token
);
verifyCredentials = () => this.request("/1/account/verify_credentials");
bookmarks = {
list: (params = {}) => this.request(
"/1/bookmarks/list",
params
),
updateReadProgress: (params) => this.request(
"/1/bookmarks/update_read_progress",
params
),
add: (params) => this.request("/1/bookmarks/add", params),
delete: (bookmark_id) => this.request("/1/bookmarks/delete", { bookmark_id }),
star: (bookmark_id) => this.request("/1/bookmarks/star", { bookmark_id }),
unstar: (bookmark_id) => this.request("/1/bookmarks/unstar", { bookmark_id }),
archive: (bookmark_id) => this.request("/1/bookmarks/archive", { bookmark_id }),
unarchive: (bookmark_id) => this.request("/1/bookmarks/unarchive", { bookmark_id }),
move: (bookmark_id, folder_id) => this.request("/1/bookmarks/move", {
bookmark_id,
folder_id
}),
getText: (bookmark_id) => this.request("/1/bookmarks/get_text", { bookmark_id })
};
folders = {
list: () => this.request("/1/folders/list"),
add: (title) => this.request("/1/folders/add", { title }),
delete: (folder_id) => this.request("/1/folders/delete", { folder_id }),
setOrder: (order) => this.request("/1/folders/set_order", { order })
};
highlights = {
list: (bookmark_id) => this.request(
`/1.1/bookmarks/${bookmark_id}/highlights`
),
add: (params) => this.request(
`/1.1/bookmarks/${params.bookmark_id}/highlight`,
{ text: params.text, position: params.position }
),
delete: (highlight_id) => this.request(`/1.1/highlights/${highlight_id}/delete`)
};
};
export {
Instapaper
};