instapaper-ts 1.1.3 → 2.0.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 +68 -37
- package/dist/index.d.ts +10 -13
- package/dist/index.js +43 -74
- package/package.json +34 -35
package/README.md
CHANGED
|
@@ -1,68 +1,99 @@
|
|
|
1
|
-
# instapaper
|
|
1
|
+
# instapaper
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Supports all available endpoints from the [Full Developer API](https://www.instapaper.com/api)!
|
|
6
|
-
|
|
7
|
-
---
|
|
3
|
+
A TypeScript client for the [Instapaper API](https://www.instapaper.com/api).
|
|
8
4
|
|
|
9
5
|
## Installation
|
|
10
6
|
|
|
11
|
-
```
|
|
12
|
-
npm install
|
|
7
|
+
```bash
|
|
8
|
+
npm install instapaper-ts
|
|
13
9
|
```
|
|
14
10
|
|
|
15
|
-
##
|
|
11
|
+
## Authentication
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
import { Instapaper } from "instapaper-ts";
|
|
13
|
+
Instapaper uses OAuth 1.0a with xAuth. You'll need a **consumer key and secret** from Instapaper, plus your account **username and password**, but only to perform a one-time token exchange. After that, store the token and use it directly for all future requests.
|
|
19
14
|
|
|
20
|
-
|
|
15
|
+
### 1. Exchange credentials for a token
|
|
21
16
|
|
|
22
|
-
|
|
17
|
+
Call `Instapaper.fetchToken(...)` to authenticate with your username and password. This performs the xAuth token exchange and returns the result. Once you have the token, persist it so you don't repeat the exchange on every run.
|
|
23
18
|
|
|
24
|
-
|
|
19
|
+
```typescript
|
|
20
|
+
import { Instapaper } from "instapaper";
|
|
25
21
|
|
|
26
|
-
|
|
22
|
+
const token = await Instapaper.fetchToken({
|
|
23
|
+
consumerKey: "your_consumer_key",
|
|
24
|
+
consumerSecret: "your_consumer_secret",
|
|
25
|
+
username: "your@email.com",
|
|
26
|
+
password: "your_password",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Persist the token for future use
|
|
30
|
+
await saveToken(token); // e.g. write to disk, a secrets store, a session cookie etc.
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 2. Initialise from a cached token
|
|
34
|
+
|
|
35
|
+
Once you have a token, you can then use it to instantiate the Instapaper client. This allows you to reuse your token in different parts of your app, without having to reauthenticate every time with your username and password.
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
const token = await loadToken();
|
|
39
|
+
|
|
40
|
+
const client = new Instapaper({
|
|
41
|
+
consumerKey: "your_consumer_key",
|
|
42
|
+
consumerSecret: "your_consumer_secret",
|
|
43
|
+
token,
|
|
44
|
+
});
|
|
27
45
|
```
|
|
28
46
|
|
|
29
47
|
## Methods
|
|
30
48
|
|
|
31
|
-
See [
|
|
49
|
+
See the [full API documentation](https://www.instapaper.com/api) for complete parameter and response details.
|
|
32
50
|
|
|
33
|
-
###
|
|
51
|
+
### Account
|
|
34
52
|
|
|
35
|
-
|
|
36
|
-
|
|
53
|
+
| Method | Description |
|
|
54
|
+
| --------------------- | --------------------------------------------- |
|
|
55
|
+
| `verifyCredentials()` | Verify the current token and return user info |
|
|
37
56
|
|
|
38
57
|
### Bookmarks
|
|
39
58
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
59
|
+
All bookmark methods are available under `instapaper.bookmarks.*`.
|
|
60
|
+
|
|
61
|
+
| Method | Description |
|
|
62
|
+
| ------------------------------ | --------------------------------------------------------------- |
|
|
63
|
+
| `list(params?)` | List bookmarks; optionally filter by `folder_id`, `limit`, etc. |
|
|
64
|
+
| `add(params)` | Save a URL as a new bookmark |
|
|
65
|
+
| `delete(bookmark_id)` | Permanently delete a bookmark |
|
|
66
|
+
| `updateReadProgress(params)` | Update the read progress percentage for a bookmark |
|
|
67
|
+
| `star(bookmark_id)` | Star a bookmark |
|
|
68
|
+
| `unstar(bookmark_id)` | Unstar a bookmark |
|
|
69
|
+
| `archive(bookmark_id)` | Move a bookmark to the archive |
|
|
70
|
+
| `unarchive(bookmark_id)` | Move a bookmark out of the archive |
|
|
71
|
+
| `move(bookmark_id, folder_id)` | Move a bookmark to a specific folder |
|
|
72
|
+
| `getText(bookmark_id)` | Fetch the processed text content of a bookmark |
|
|
50
73
|
|
|
51
74
|
### Folders
|
|
52
75
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
76
|
+
All folder methods are available under `instapaper.folders.*`.
|
|
77
|
+
|
|
78
|
+
| Method | Description |
|
|
79
|
+
| ------------------- | ----------------------------------- |
|
|
80
|
+
| `list()` | List all user-created folders |
|
|
81
|
+
| `add(title)` | Create a new folder |
|
|
82
|
+
| `delete(folder_id)` | Delete a folder |
|
|
83
|
+
| `setOrder(order)` | Update the display order of folders |
|
|
57
84
|
|
|
58
85
|
### Highlights
|
|
59
86
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
87
|
+
All highlight methods are available under `instapaper.highlights.*`.
|
|
88
|
+
|
|
89
|
+
| Method | Description |
|
|
90
|
+
| ---------------------- | ---------------------------------- |
|
|
91
|
+
| `list(bookmark_id)` | List all highlights for a bookmark |
|
|
92
|
+
| `add(params)` | Add a highlight to a bookmark |
|
|
93
|
+
| `delete(highlight_id)` | Delete a highlight |
|
|
63
94
|
|
|
64
95
|
---
|
|
65
96
|
|
|
66
97
|
## Terms of Use
|
|
67
98
|
|
|
68
|
-
Please
|
|
99
|
+
Please review Instapaper's [API Terms of Use](https://www.instapaper.com/api/terms) before using this library.
|
package/dist/index.d.ts
CHANGED
|
@@ -82,24 +82,21 @@ type AddHighlightParams = {
|
|
|
82
82
|
|
|
83
83
|
declare class Instapaper {
|
|
84
84
|
private baseUrl;
|
|
85
|
-
private authUrl;
|
|
86
85
|
private oauth;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
private token?;
|
|
90
|
-
constructor({ consumerKey, consumerSecret, username, password, token, }: {
|
|
86
|
+
readonly token: OAuth.Token;
|
|
87
|
+
constructor({ consumerKey, consumerSecret, token, }: {
|
|
91
88
|
consumerKey: string;
|
|
92
89
|
consumerSecret: string;
|
|
93
|
-
|
|
94
|
-
password?: string;
|
|
95
|
-
token?: OAuth.Token;
|
|
90
|
+
token: OAuth.Token;
|
|
96
91
|
});
|
|
97
|
-
|
|
98
|
-
|
|
92
|
+
static fetchToken({ consumerKey, consumerSecret, username, password, }: {
|
|
93
|
+
consumerKey: string;
|
|
94
|
+
consumerSecret: string;
|
|
95
|
+
username: string;
|
|
96
|
+
password: string;
|
|
97
|
+
}): Promise<OAuth.Token>;
|
|
98
|
+
private static postForm;
|
|
99
99
|
private request;
|
|
100
|
-
setCredentials: (username: string, password: string) => void;
|
|
101
|
-
withCredentials: (username: string, password: string) => this;
|
|
102
|
-
withToken: (token: OAuth.Token) => this;
|
|
103
100
|
verifyCredentials: () => Promise<[User]>;
|
|
104
101
|
bookmarks: {
|
|
105
102
|
list: (params?: ListParams) => Promise<(Bookmark | Folder | User | Error | Meta)[]>;
|
package/dist/index.js
CHANGED
|
@@ -1,47 +1,56 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import assert from "
|
|
3
|
-
import crypto from "
|
|
2
|
+
import assert from "assert";
|
|
3
|
+
import crypto from "crypto";
|
|
4
4
|
import OAuth from "oauth-1.0a";
|
|
5
|
-
var Instapaper = class {
|
|
5
|
+
var Instapaper = class _Instapaper {
|
|
6
6
|
baseUrl = "https://www.instapaper.com/api";
|
|
7
|
-
authUrl = this.baseUrl + "/1/oauth/access_token";
|
|
8
7
|
oauth;
|
|
9
|
-
username;
|
|
10
|
-
password;
|
|
11
8
|
token;
|
|
12
9
|
constructor({
|
|
13
10
|
consumerKey,
|
|
14
11
|
consumerSecret,
|
|
15
|
-
username,
|
|
16
|
-
password,
|
|
17
12
|
token
|
|
18
13
|
}) {
|
|
19
14
|
this.oauth = new OAuth({
|
|
20
|
-
consumer: {
|
|
21
|
-
key: consumerKey,
|
|
22
|
-
secret: consumerSecret
|
|
23
|
-
},
|
|
15
|
+
consumer: { key: consumerKey, secret: consumerSecret },
|
|
24
16
|
signature_method: "HMAC-SHA1",
|
|
25
17
|
hash_function: (base_string, key) => crypto.createHmac("sha1", key).update(base_string).digest("base64")
|
|
26
18
|
});
|
|
27
|
-
this.username = username;
|
|
28
|
-
this.password = password;
|
|
29
19
|
this.token = token;
|
|
30
20
|
}
|
|
31
|
-
|
|
21
|
+
static async fetchToken({
|
|
22
|
+
consumerKey,
|
|
23
|
+
consumerSecret,
|
|
24
|
+
username,
|
|
25
|
+
password
|
|
26
|
+
}) {
|
|
27
|
+
const oauth = new OAuth({
|
|
28
|
+
consumer: { key: consumerKey, secret: consumerSecret },
|
|
29
|
+
signature_method: "HMAC-SHA1",
|
|
30
|
+
hash_function: (base_string, key2) => crypto.createHmac("sha1", key2).update(base_string).digest("base64")
|
|
31
|
+
});
|
|
32
|
+
const responseText = await _Instapaper.postForm(
|
|
33
|
+
oauth,
|
|
34
|
+
"https://www.instapaper.com/api/1/oauth/access_token",
|
|
35
|
+
{
|
|
36
|
+
x_auth_username: username,
|
|
37
|
+
x_auth_password: password,
|
|
38
|
+
x_auth_mode: "client_auth"
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
const data = new URLSearchParams(responseText);
|
|
42
|
+
const key = data.get("oauth_token");
|
|
43
|
+
const secret = data.get("oauth_token_secret");
|
|
44
|
+
assert(key && secret, "Token exchange failed: key or secret is null.");
|
|
45
|
+
return { key, secret };
|
|
46
|
+
}
|
|
47
|
+
static async postForm(oauth, url, params = {}, token) {
|
|
32
48
|
const form = new URLSearchParams();
|
|
33
49
|
for (const [key, val] of Object.entries(params)) {
|
|
34
50
|
form.append(key, String(val));
|
|
35
51
|
}
|
|
36
|
-
const headers =
|
|
37
|
-
|
|
38
|
-
{
|
|
39
|
-
url,
|
|
40
|
-
method: "POST",
|
|
41
|
-
data: params
|
|
42
|
-
},
|
|
43
|
-
token
|
|
44
|
-
)
|
|
52
|
+
const headers = oauth.toHeader(
|
|
53
|
+
oauth.authorize({ url, method: "POST", data: params }, token)
|
|
45
54
|
);
|
|
46
55
|
const response = await fetch(url, {
|
|
47
56
|
method: "POST",
|
|
@@ -52,52 +61,15 @@ var Instapaper = class {
|
|
|
52
61
|
const errorText = await response.text();
|
|
53
62
|
throw new Error(`API error: ${response.status} ${errorText}`);
|
|
54
63
|
}
|
|
55
|
-
const contentType = response.headers.get("content-type")
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
this.username && this.password,
|
|
65
|
-
"Please set username and password with setCredentials()."
|
|
66
|
-
);
|
|
67
|
-
const params = {
|
|
68
|
-
x_auth_username: this.username,
|
|
69
|
-
x_auth_password: this.password,
|
|
70
|
-
x_auth_mode: "client_auth"
|
|
71
|
-
};
|
|
72
|
-
const responseText = await this.makeRequest(this.authUrl, params);
|
|
73
|
-
const data = new URLSearchParams(responseText);
|
|
74
|
-
const key = data.get("oauth_token");
|
|
75
|
-
const secret = data.get("oauth_token_secret");
|
|
76
|
-
assert(
|
|
77
|
-
key && secret,
|
|
78
|
-
"There was an error fetching the token. One or both of key and secret is null."
|
|
79
|
-
);
|
|
80
|
-
return { key, secret };
|
|
81
|
-
};
|
|
82
|
-
request = async (endpoint, params = {}) => {
|
|
83
|
-
if (!this.token) {
|
|
84
|
-
this.token = await this.fetchToken();
|
|
85
|
-
}
|
|
86
|
-
const url = this.baseUrl + endpoint;
|
|
87
|
-
return this.makeRequest(url, params, this.token);
|
|
88
|
-
};
|
|
89
|
-
setCredentials = (username, password) => {
|
|
90
|
-
this.username = username;
|
|
91
|
-
this.password = password;
|
|
92
|
-
};
|
|
93
|
-
withCredentials = (username, password) => {
|
|
94
|
-
this.setCredentials(username, password);
|
|
95
|
-
return this;
|
|
96
|
-
};
|
|
97
|
-
withToken = (token) => {
|
|
98
|
-
this.token = token;
|
|
99
|
-
return this;
|
|
100
|
-
};
|
|
64
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
65
|
+
return contentType.includes("application/json") ? response.json() : response.text();
|
|
66
|
+
}
|
|
67
|
+
request = (endpoint, params = {}) => _Instapaper.postForm(
|
|
68
|
+
this.oauth,
|
|
69
|
+
this.baseUrl + endpoint,
|
|
70
|
+
params,
|
|
71
|
+
this.token
|
|
72
|
+
);
|
|
101
73
|
verifyCredentials = () => this.request("/1/account/verify_credentials");
|
|
102
74
|
bookmarks = {
|
|
103
75
|
list: (params = {}) => this.request(
|
|
@@ -132,10 +104,7 @@ var Instapaper = class {
|
|
|
132
104
|
),
|
|
133
105
|
add: (params) => this.request(
|
|
134
106
|
`/1.1/bookmarks/${params.bookmark_id}/highlight`,
|
|
135
|
-
{
|
|
136
|
-
text: params.text,
|
|
137
|
-
position: params.position
|
|
138
|
-
}
|
|
107
|
+
{ text: params.text, position: params.position }
|
|
139
108
|
),
|
|
140
109
|
delete: (highlight_id) => this.request(`/1.1/highlights/${highlight_id}/delete`)
|
|
141
110
|
};
|
package/package.json
CHANGED
|
@@ -1,37 +1,36 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
2
|
+
"name": "instapaper-ts",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "A type-safe API client for Instapaper.",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"/dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsup",
|
|
13
|
+
"dev": "tsup --watch"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"instapaper",
|
|
17
|
+
"read",
|
|
18
|
+
"later",
|
|
19
|
+
"typescript",
|
|
20
|
+
"library"
|
|
21
|
+
],
|
|
22
|
+
"author": "Ben Silverman (bensilverman.co.uk)",
|
|
23
|
+
"homepage": "https://github.com/benslv/instapaper-ts",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/benslv/instapaper-ts/issues"
|
|
26
|
+
},
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"oauth-1.0a": "^2.2.6"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^26.0.0",
|
|
33
|
+
"tsup": "^8.5.1",
|
|
34
|
+
"typescript": "^6.0.3"
|
|
35
|
+
}
|
|
37
36
|
}
|