instapaper-ts
Version:
A type-safe API client for Instapaper.
146 lines (145 loc) • 4.44 kB
JavaScript
// src/index.ts
import assert from "node:assert";
import crypto from "node:crypto";
import OAuth from "oauth-1.0a";
var Instapaper = class {
baseUrl = "https://www.instapaper.com/api";
authUrl = this.baseUrl + "/1/oauth/access_token";
oauth;
username;
password;
token;
constructor({
consumerKey,
consumerSecret,
username,
password,
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.username = username;
this.password = password;
this.token = token;
}
makeRequest = async (url, params = {}, token) => {
const form = new URLSearchParams();
for (const [key, val] of Object.entries(params)) {
form.append(key, String(val));
}
const headers = this.oauth.toHeader(
this.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") || "";
if (contentType.includes("application/json")) {
return response.json();
} else {
return response.text();
}
};
fetchToken = async () => {
assert(
this.username && this.password,
"Please set username and password with setCredentials()."
);
const params = {
x_auth_username: this.username,
x_auth_password: this.password,
x_auth_mode: "client_auth"
};
const responseText = await this.makeRequest(this.authUrl, params);
const data = new URLSearchParams(responseText);
const key = data.get("oauth_token");
const secret = data.get("oauth_token_secret");
assert(
key && secret,
"There was an error fetching the token. One or both of key and secret is null."
);
return { key, secret };
};
request = async (endpoint, params = {}) => {
if (!this.token) {
this.token = await this.fetchToken();
}
const url = this.baseUrl + endpoint;
return this.makeRequest(url, params, this.token);
};
setCredentials = (username, password) => {
this.username = username;
this.password = password;
};
withCredentials = (username, password) => {
this.setCredentials(username, password);
return this;
};
withToken = (token) => {
this.token = token;
return this;
};
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
};