-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathindex.ts
131 lines (119 loc) · 3.11 KB
/
index.ts
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import axios from "axios";
import fs from "fs";
const SEARCH_TERM = process.env.SEARCH_TERM ?? "";
const API_URL =
process.env.OMNIVORE_API_URL ?? "https://api-prod.omnivore.app/api/graphql";
type LibraryItems = {
edges: LibraryItem[];
pageInfo: PageInfo;
errorCodes?: string[];
};
export type LibraryItem = {
cursor: string;
node: LibraryItemNode;
};
export type LibraryItemNode = {
slug: string;
content: string;
highlights: Highlight[];
};
export type PageInfo = {
hasNextPage: boolean;
endCursor: string;
totalCount: number;
};
export type Highlight = {
id: string;
quote: string;
annotation?: string;
};
// Omnivore's `articles` API returns pages of articles. Here we specify to return
// a page of `limit` items starting at `cursor`. The `cursor` is the last item
// in the previous page of data fetched (or undefined if this is the first page).
//
// Here we request an articles `id`, `title`, `originalArticleUrl`, and its
// highlights. For highlights we just request the `id`, `quote`, and `annotation`.
// The `quote` is the section of text highlighted, and `annotation` is an optional
// comment that the user can add to the highlight.
const fetchPage = async (
cursor: string | undefined,
limit: number,
searchQuery: string
): Promise<LibraryItems> => {
const data = JSON.stringify({
variables: {
after: cursor,
first: limit,
format: "markdown",
includeContent: true,
query: searchQuery,
},
query: `query Search(
$after: String
$first: Int
$query: String
$includeContent: Boolean
$format: String
) {
search(
after: $after
first: $first
query: $query
includeContent: $includeContent
format: $format
) {
... on SearchSuccess {
edges {
node {
slug
content
}
}
pageInfo {
hasNextPage
endCursor
totalCount
}
}
... on SearchError {
errorCodes
}
}
}
`,
});
const response = await axios.post(`${API_URL}/graphql`, data, {
headers: {
Cookie: `auth=${process.env.OMNIVORE_AUTH_TOKEN};`,
"Content-Type": "application/json",
},
});
return response.data.data.search as LibraryItems;
};
// This iterator handles pagination of the Omnivore API. It will fetch all items
// matching the search query.
async function* fetchAllLinks(start: string | undefined, searchQuery: string) {
let cursor = start;
let hasNextPage = true;
while (hasNextPage) {
const nextPage = await fetchPage(cursor, 10, searchQuery);
for (const edge of nextPage.edges) {
yield edge.node;
}
cursor = nextPage.pageInfo.endCursor;
hasNextPage = nextPage.pageInfo.hasNextPage;
}
}
(async function () {
try {
fs.mkdirSync("documents");
} catch {}
var downloaded = 0;
for await (const item of fetchAllLinks(undefined, SEARCH_TERM)) {
fs.writeFileSync(`documents/${item.slug}.md`, item.content);
downloaded = downloaded + 1;
if (downloaded > 20) {
break;
}
}
})();