@taisan11/zenn.js 0.1.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 +15 -0
- package/dist/client.d.ts +40 -0
- package/dist/index.cjs +172 -0
- package/dist/index.cjs.map +10 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +130 -0
- package/dist/index.js.map +10 -0
- package/dist/types.d.ts +468 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# zenn.js
|
|
2
|
+
|
|
3
|
+
To install dependencies:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bun install
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
To run:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun run index.ts
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This project was created using `bun init` in bun v1.3.14. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ArticleDetail, ArticleResponse, ArticlesResponse, BookResponse, BooksResponse, ChapterResponse, ClientOptions, ListArticlesOptions, ListBooksOptions, ListFollowersOptions, ListScrapsOptions, ListUsersCommentsOptions, PublicationMini, PublicationResponse, ScrapsResponse, ScrapResponse, SearchOptions, SearchResponse, SearchSource, TopicResponse, TopicsResponse, UserCommentsResponse, UserResponse, UsersResponse, Event } from "./types";
|
|
2
|
+
export declare class ZennApiError extends Error {
|
|
3
|
+
status: number;
|
|
4
|
+
body: unknown;
|
|
5
|
+
constructor(status: number, body: unknown, message: string);
|
|
6
|
+
}
|
|
7
|
+
export interface PublicationsMiniResponse {
|
|
8
|
+
publications: PublicationMini[];
|
|
9
|
+
next_page: number | null;
|
|
10
|
+
}
|
|
11
|
+
export declare class ZennClient {
|
|
12
|
+
private readonly baseUrl;
|
|
13
|
+
private readonly fetcher;
|
|
14
|
+
private readonly headers;
|
|
15
|
+
constructor(options?: ClientOptions);
|
|
16
|
+
private request;
|
|
17
|
+
listArticles(options?: ListArticlesOptions): Promise<ArticlesResponse>;
|
|
18
|
+
getArticle(slug: string): Promise<ArticleResponse>;
|
|
19
|
+
getArticleById(id: number): Promise<ArticleResponse>;
|
|
20
|
+
iterArticles(options?: Omit<ListArticlesOptions, "page">): AsyncGenerator<ArticleDetail, void, void>;
|
|
21
|
+
listBooks(options?: ListBooksOptions): Promise<BooksResponse>;
|
|
22
|
+
getBook(slug: string): Promise<BookResponse>;
|
|
23
|
+
getChapter(id: number): Promise<ChapterResponse>;
|
|
24
|
+
listScraps(options?: ListScrapsOptions): Promise<ScrapsResponse>;
|
|
25
|
+
getScrap(slug: string): Promise<ScrapResponse>;
|
|
26
|
+
getUser(username: string): Promise<UserResponse>;
|
|
27
|
+
getCurrentUser(): Promise<UserResponse>;
|
|
28
|
+
getUserFollowers(username: string, options?: ListFollowersOptions): Promise<UsersResponse>;
|
|
29
|
+
getUserFollowingsUsers(username: string, options?: ListFollowersOptions): Promise<UsersResponse>;
|
|
30
|
+
getUserFollowingsPublications(username: string, options?: ListFollowersOptions): Promise<PublicationsMiniResponse>;
|
|
31
|
+
getUserComments(username: string, options?: ListUsersCommentsOptions): Promise<UserCommentsResponse>;
|
|
32
|
+
getPublication(name: string): Promise<PublicationResponse>;
|
|
33
|
+
getPublicationFollowers(name: string, options?: ListFollowersOptions): Promise<UsersResponse>;
|
|
34
|
+
listTopics(): Promise<TopicsResponse>;
|
|
35
|
+
getTopic(name: string): Promise<TopicResponse>;
|
|
36
|
+
search<TSource extends SearchSource>(options: SearchOptions & {
|
|
37
|
+
source: TSource;
|
|
38
|
+
}): Promise<SearchResponse<TSource>>;
|
|
39
|
+
listEvents(): Promise<Event[]>;
|
|
40
|
+
}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
function __accessProp(key) {
|
|
6
|
+
return this[key];
|
|
7
|
+
}
|
|
8
|
+
var __toCommonJS = (from) => {
|
|
9
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
10
|
+
if (entry)
|
|
11
|
+
return entry;
|
|
12
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (var key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(entry, key))
|
|
16
|
+
__defProp(entry, key, {
|
|
17
|
+
get: __accessProp.bind(from, key),
|
|
18
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
__moduleCache.set(from, entry);
|
|
22
|
+
return entry;
|
|
23
|
+
};
|
|
24
|
+
var __moduleCache;
|
|
25
|
+
var __returnValue = (v) => v;
|
|
26
|
+
function __exportSetter(name, newValue) {
|
|
27
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
28
|
+
}
|
|
29
|
+
var __export = (target, all) => {
|
|
30
|
+
for (var name in all)
|
|
31
|
+
__defProp(target, name, {
|
|
32
|
+
get: all[name],
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
set: __exportSetter.bind(all, name)
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// src/index.ts
|
|
40
|
+
var exports_src = {};
|
|
41
|
+
__export(exports_src, {
|
|
42
|
+
ZennClient: () => ZennClient,
|
|
43
|
+
ZennApiError: () => ZennApiError
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(exports_src);
|
|
46
|
+
|
|
47
|
+
// src/client.ts
|
|
48
|
+
var DEFAULT_BASE_URL = "https://zenn.dev";
|
|
49
|
+
|
|
50
|
+
class ZennApiError extends Error {
|
|
51
|
+
status;
|
|
52
|
+
body;
|
|
53
|
+
constructor(status, body, message) {
|
|
54
|
+
super(message);
|
|
55
|
+
this.name = "ZennApiError";
|
|
56
|
+
this.status = status;
|
|
57
|
+
this.body = body;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function buildQuery(params) {
|
|
61
|
+
if (!params)
|
|
62
|
+
return "";
|
|
63
|
+
const search = new URLSearchParams;
|
|
64
|
+
for (const [k, v] of Object.entries(params)) {
|
|
65
|
+
if (v === undefined || v === null || v === "")
|
|
66
|
+
continue;
|
|
67
|
+
search.set(k, String(v));
|
|
68
|
+
}
|
|
69
|
+
const qs = search.toString();
|
|
70
|
+
return qs.length > 0 ? `?${qs}` : "";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
class ZennClient {
|
|
74
|
+
baseUrl;
|
|
75
|
+
fetcher;
|
|
76
|
+
headers;
|
|
77
|
+
constructor(options = {}) {
|
|
78
|
+
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
79
|
+
this.fetcher = options.fetch ?? fetch;
|
|
80
|
+
this.headers = options.headers ?? {};
|
|
81
|
+
}
|
|
82
|
+
async request(path, params) {
|
|
83
|
+
const url = `${this.baseUrl}${path}${buildQuery(params)}`;
|
|
84
|
+
const res = await this.fetcher(url, {
|
|
85
|
+
headers: {
|
|
86
|
+
Accept: "application/json",
|
|
87
|
+
...this.headers
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
const text = await res.text();
|
|
91
|
+
const data = text.length > 0 ? JSON.parse(text) : null;
|
|
92
|
+
if (!res.ok) {
|
|
93
|
+
throw new ZennApiError(res.status, data, `Zenn API request failed: ${res.status} ${res.statusText} (${url})`);
|
|
94
|
+
}
|
|
95
|
+
return data;
|
|
96
|
+
}
|
|
97
|
+
listArticles(options = {}) {
|
|
98
|
+
return this.request("/api/articles", options);
|
|
99
|
+
}
|
|
100
|
+
getArticle(slug) {
|
|
101
|
+
return this.request(`/api/articles/${encodeURIComponent(slug)}`);
|
|
102
|
+
}
|
|
103
|
+
getArticleById(id) {
|
|
104
|
+
return this.request("/api/articles", { id });
|
|
105
|
+
}
|
|
106
|
+
async* iterArticles(options = {}) {
|
|
107
|
+
let page = 1;
|
|
108
|
+
while (true) {
|
|
109
|
+
const res = await this.listArticles({ ...options, page });
|
|
110
|
+
for (const item of res.articles) {
|
|
111
|
+
const detail = await this.getArticle(item.slug);
|
|
112
|
+
yield detail.article;
|
|
113
|
+
}
|
|
114
|
+
if (res.next_page === null || res.next_page === undefined)
|
|
115
|
+
break;
|
|
116
|
+
page = res.next_page;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
listBooks(options = {}) {
|
|
120
|
+
return this.request("/api/books", options);
|
|
121
|
+
}
|
|
122
|
+
getBook(slug) {
|
|
123
|
+
return this.request(`/api/books/${encodeURIComponent(slug)}`);
|
|
124
|
+
}
|
|
125
|
+
getChapter(id) {
|
|
126
|
+
return this.request(`/api/chapters/${id}`);
|
|
127
|
+
}
|
|
128
|
+
listScraps(options = {}) {
|
|
129
|
+
return this.request("/api/scraps", options);
|
|
130
|
+
}
|
|
131
|
+
getScrap(slug) {
|
|
132
|
+
return this.request(`/api/scraps/${encodeURIComponent(slug)}`);
|
|
133
|
+
}
|
|
134
|
+
getUser(username) {
|
|
135
|
+
return this.request(`/api/users/${encodeURIComponent(username)}`);
|
|
136
|
+
}
|
|
137
|
+
getCurrentUser() {
|
|
138
|
+
return this.request("/api/users/current");
|
|
139
|
+
}
|
|
140
|
+
getUserFollowers(username, options = {}) {
|
|
141
|
+
return this.request(`/api/users/${encodeURIComponent(username)}/followers`, options);
|
|
142
|
+
}
|
|
143
|
+
getUserFollowingsUsers(username, options = {}) {
|
|
144
|
+
return this.request(`/api/users/${encodeURIComponent(username)}/followings/users`, options);
|
|
145
|
+
}
|
|
146
|
+
getUserFollowingsPublications(username, options = {}) {
|
|
147
|
+
return this.request(`/api/users/${encodeURIComponent(username)}/followings/publications`, options);
|
|
148
|
+
}
|
|
149
|
+
getUserComments(username, options = {}) {
|
|
150
|
+
return this.request(`/api/users/${encodeURIComponent(username)}/comments`, options);
|
|
151
|
+
}
|
|
152
|
+
getPublication(name) {
|
|
153
|
+
return this.request(`/api/publications/${encodeURIComponent(name)}`);
|
|
154
|
+
}
|
|
155
|
+
getPublicationFollowers(name, options = {}) {
|
|
156
|
+
return this.request(`/api/publications/${encodeURIComponent(name)}/followers`, options);
|
|
157
|
+
}
|
|
158
|
+
listTopics() {
|
|
159
|
+
return this.request("/api/topics");
|
|
160
|
+
}
|
|
161
|
+
getTopic(name) {
|
|
162
|
+
return this.request(`/api/topics/${encodeURIComponent(name)}`);
|
|
163
|
+
}
|
|
164
|
+
search(options) {
|
|
165
|
+
return this.request("/api/search", options);
|
|
166
|
+
}
|
|
167
|
+
listEvents() {
|
|
168
|
+
return this.request("/api/events");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
//# debugId=A4B2F835B9C68E8564756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/client.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type {\n ArticleDetail,\n ArticleResponse,\n ArticlesResponse,\n BookResponse,\n BooksResponse,\n ChapterResponse,\n ClientOptions,\n ListArticlesOptions,\n ListBooksOptions,\n ListFollowersOptions,\n ListScrapsOptions,\n ListUsersCommentsOptions,\n PublicationMini,\n PublicationResponse,\n ScrapsResponse,\n ScrapResponse,\n SearchOptions,\n SearchResponse,\n SearchSource,\n TopicResponse,\n TopicsResponse,\n UserCommentsResponse,\n UserResponse,\n UsersResponse,\n Event,\n} from \"./types.ts\";\n\nconst DEFAULT_BASE_URL = \"https://zenn.dev\";\n\nexport class ZennApiError extends Error {\n status: number;\n body: unknown;\n\n constructor(status: number, body: unknown, message: string) {\n super(message);\n this.name = \"ZennApiError\";\n this.status = status;\n this.body = body;\n }\n}\n\nexport interface PublicationsMiniResponse {\n publications: PublicationMini[];\n next_page: number | null;\n}\n\nfunction buildQuery(params: object | undefined): string {\n if (!params) return \"\";\n const search = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v === undefined || v === null || v === \"\") continue;\n search.set(k, String(v));\n }\n const qs = search.toString();\n return qs.length > 0 ? `?${qs}` : \"\";\n}\n\nexport class ZennClient {\n private readonly baseUrl: string;\n private readonly fetcher: typeof fetch;\n private readonly headers: HeadersInit;\n\n constructor(options: ClientOptions = {}) {\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.fetcher = options.fetch ?? fetch;\n this.headers = options.headers ?? {};\n }\n\n private async request<T>(path: string, params?: object): Promise<T> {\n const url = `${this.baseUrl}${path}${buildQuery(params)}`;\n const res = await this.fetcher(url, {\n headers: {\n Accept: \"application/json\",\n ...this.headers,\n },\n });\n const text = await res.text();\n const data: unknown = text.length > 0 ? JSON.parse(text) : null;\n if (!res.ok) {\n throw new ZennApiError(res.status, data, `Zenn API request failed: ${res.status} ${res.statusText} (${url})`);\n }\n return data as T;\n }\n\n // ---------- Articles ----------\n\n listArticles(options: ListArticlesOptions = {}): Promise<ArticlesResponse> {\n return this.request<ArticlesResponse>(\"/api/articles\", options);\n }\n\n getArticle(slug: string): Promise<ArticleResponse> {\n return this.request<ArticleResponse>(`/api/articles/${encodeURIComponent(slug)}`);\n }\n\n getArticleById(id: number): Promise<ArticleResponse> {\n return this.request<ArticleResponse>(\"/api/articles\", { id });\n }\n\n async *iterArticles(options: Omit<ListArticlesOptions, \"page\"> = {}): AsyncGenerator<ArticleDetail, void, void> {\n let page = 1;\n while (true) {\n const res = await this.listArticles({ ...options, page });\n for (const item of res.articles) {\n const detail = await this.getArticle(item.slug);\n yield detail.article;\n }\n if (res.next_page === null || res.next_page === undefined) break;\n page = res.next_page;\n }\n }\n\n // ---------- Books ----------\n\n listBooks(options: ListBooksOptions = {}): Promise<BooksResponse> {\n return this.request<BooksResponse>(\"/api/books\", options);\n }\n\n getBook(slug: string): Promise<BookResponse> {\n return this.request<BookResponse>(`/api/books/${encodeURIComponent(slug)}`);\n }\n\n getChapter(id: number): Promise<ChapterResponse> {\n return this.request<ChapterResponse>(`/api/chapters/${id}`);\n }\n\n // ---------- Scraps ----------\n\n listScraps(options: ListScrapsOptions = {}): Promise<ScrapsResponse> {\n return this.request<ScrapsResponse>(\"/api/scraps\", options);\n }\n\n getScrap(slug: string): Promise<ScrapResponse> {\n return this.request<ScrapResponse>(`/api/scraps/${encodeURIComponent(slug)}`);\n }\n\n // ---------- Users ----------\n\n getUser(username: string): Promise<UserResponse> {\n return this.request<UserResponse>(`/api/users/${encodeURIComponent(username)}`);\n }\n\n getCurrentUser(): Promise<UserResponse> {\n return this.request<UserResponse>(\"/api/users/current\");\n }\n\n getUserFollowers(username: string, options: ListFollowersOptions = {}): Promise<UsersResponse> {\n return this.request<UsersResponse>(`/api/users/${encodeURIComponent(username)}/followers`, options);\n }\n\n getUserFollowingsUsers(username: string, options: ListFollowersOptions = {}): Promise<UsersResponse> {\n return this.request<UsersResponse>(`/api/users/${encodeURIComponent(username)}/followings/users`, options);\n }\n\n getUserFollowingsPublications(\n username: string,\n options: ListFollowersOptions = {},\n ): Promise<PublicationsMiniResponse> {\n return this.request<PublicationsMiniResponse>(\n `/api/users/${encodeURIComponent(username)}/followings/publications`,\n options,\n );\n }\n\n getUserComments(username: string, options: ListUsersCommentsOptions = {}): Promise<UserCommentsResponse> {\n return this.request<UserCommentsResponse>(`/api/users/${encodeURIComponent(username)}/comments`, options);\n }\n\n // ---------- Publications ----------\n\n getPublication(name: string): Promise<PublicationResponse> {\n return this.request<PublicationResponse>(`/api/publications/${encodeURIComponent(name)}`);\n }\n\n getPublicationFollowers(name: string, options: ListFollowersOptions = {}): Promise<UsersResponse> {\n return this.request<UsersResponse>(`/api/publications/${encodeURIComponent(name)}/followers`, options);\n }\n\n // ---------- Topics ----------\n\n listTopics(): Promise<TopicsResponse> {\n return this.request<TopicsResponse>(\"/api/topics\");\n }\n\n getTopic(name: string): Promise<TopicResponse> {\n return this.request<TopicResponse>(`/api/topics/${encodeURIComponent(name)}`);\n }\n\n // ---------- Search ----------\n\n search<TSource extends SearchSource>(\n options: SearchOptions & { source: TSource },\n ): Promise<SearchResponse<TSource>> {\n return this.request<SearchResponse<TSource>>(\"/api/search\", options);\n }\n\n // ---------- Events ----------\n\n listEvents(): Promise<Event[]> {\n return this.request<Event[]>(\"/api/events\");\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "A4B2F835B9C68E8564756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
var DEFAULT_BASE_URL = "https://zenn.dev";
|
|
3
|
+
|
|
4
|
+
class ZennApiError extends Error {
|
|
5
|
+
status;
|
|
6
|
+
body;
|
|
7
|
+
constructor(status, body, message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "ZennApiError";
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.body = body;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function buildQuery(params) {
|
|
15
|
+
if (!params)
|
|
16
|
+
return "";
|
|
17
|
+
const search = new URLSearchParams;
|
|
18
|
+
for (const [k, v] of Object.entries(params)) {
|
|
19
|
+
if (v === undefined || v === null || v === "")
|
|
20
|
+
continue;
|
|
21
|
+
search.set(k, String(v));
|
|
22
|
+
}
|
|
23
|
+
const qs = search.toString();
|
|
24
|
+
return qs.length > 0 ? `?${qs}` : "";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class ZennClient {
|
|
28
|
+
baseUrl;
|
|
29
|
+
fetcher;
|
|
30
|
+
headers;
|
|
31
|
+
constructor(options = {}) {
|
|
32
|
+
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
33
|
+
this.fetcher = options.fetch ?? fetch;
|
|
34
|
+
this.headers = options.headers ?? {};
|
|
35
|
+
}
|
|
36
|
+
async request(path, params) {
|
|
37
|
+
const url = `${this.baseUrl}${path}${buildQuery(params)}`;
|
|
38
|
+
const res = await this.fetcher(url, {
|
|
39
|
+
headers: {
|
|
40
|
+
Accept: "application/json",
|
|
41
|
+
...this.headers
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
const text = await res.text();
|
|
45
|
+
const data = text.length > 0 ? JSON.parse(text) : null;
|
|
46
|
+
if (!res.ok) {
|
|
47
|
+
throw new ZennApiError(res.status, data, `Zenn API request failed: ${res.status} ${res.statusText} (${url})`);
|
|
48
|
+
}
|
|
49
|
+
return data;
|
|
50
|
+
}
|
|
51
|
+
listArticles(options = {}) {
|
|
52
|
+
return this.request("/api/articles", options);
|
|
53
|
+
}
|
|
54
|
+
getArticle(slug) {
|
|
55
|
+
return this.request(`/api/articles/${encodeURIComponent(slug)}`);
|
|
56
|
+
}
|
|
57
|
+
getArticleById(id) {
|
|
58
|
+
return this.request("/api/articles", { id });
|
|
59
|
+
}
|
|
60
|
+
async* iterArticles(options = {}) {
|
|
61
|
+
let page = 1;
|
|
62
|
+
while (true) {
|
|
63
|
+
const res = await this.listArticles({ ...options, page });
|
|
64
|
+
for (const item of res.articles) {
|
|
65
|
+
const detail = await this.getArticle(item.slug);
|
|
66
|
+
yield detail.article;
|
|
67
|
+
}
|
|
68
|
+
if (res.next_page === null || res.next_page === undefined)
|
|
69
|
+
break;
|
|
70
|
+
page = res.next_page;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
listBooks(options = {}) {
|
|
74
|
+
return this.request("/api/books", options);
|
|
75
|
+
}
|
|
76
|
+
getBook(slug) {
|
|
77
|
+
return this.request(`/api/books/${encodeURIComponent(slug)}`);
|
|
78
|
+
}
|
|
79
|
+
getChapter(id) {
|
|
80
|
+
return this.request(`/api/chapters/${id}`);
|
|
81
|
+
}
|
|
82
|
+
listScraps(options = {}) {
|
|
83
|
+
return this.request("/api/scraps", options);
|
|
84
|
+
}
|
|
85
|
+
getScrap(slug) {
|
|
86
|
+
return this.request(`/api/scraps/${encodeURIComponent(slug)}`);
|
|
87
|
+
}
|
|
88
|
+
getUser(username) {
|
|
89
|
+
return this.request(`/api/users/${encodeURIComponent(username)}`);
|
|
90
|
+
}
|
|
91
|
+
getCurrentUser() {
|
|
92
|
+
return this.request("/api/users/current");
|
|
93
|
+
}
|
|
94
|
+
getUserFollowers(username, options = {}) {
|
|
95
|
+
return this.request(`/api/users/${encodeURIComponent(username)}/followers`, options);
|
|
96
|
+
}
|
|
97
|
+
getUserFollowingsUsers(username, options = {}) {
|
|
98
|
+
return this.request(`/api/users/${encodeURIComponent(username)}/followings/users`, options);
|
|
99
|
+
}
|
|
100
|
+
getUserFollowingsPublications(username, options = {}) {
|
|
101
|
+
return this.request(`/api/users/${encodeURIComponent(username)}/followings/publications`, options);
|
|
102
|
+
}
|
|
103
|
+
getUserComments(username, options = {}) {
|
|
104
|
+
return this.request(`/api/users/${encodeURIComponent(username)}/comments`, options);
|
|
105
|
+
}
|
|
106
|
+
getPublication(name) {
|
|
107
|
+
return this.request(`/api/publications/${encodeURIComponent(name)}`);
|
|
108
|
+
}
|
|
109
|
+
getPublicationFollowers(name, options = {}) {
|
|
110
|
+
return this.request(`/api/publications/${encodeURIComponent(name)}/followers`, options);
|
|
111
|
+
}
|
|
112
|
+
listTopics() {
|
|
113
|
+
return this.request("/api/topics");
|
|
114
|
+
}
|
|
115
|
+
getTopic(name) {
|
|
116
|
+
return this.request(`/api/topics/${encodeURIComponent(name)}`);
|
|
117
|
+
}
|
|
118
|
+
search(options) {
|
|
119
|
+
return this.request("/api/search", options);
|
|
120
|
+
}
|
|
121
|
+
listEvents() {
|
|
122
|
+
return this.request("/api/events");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
export {
|
|
126
|
+
ZennClient,
|
|
127
|
+
ZennApiError
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
//# debugId=5FC52C405406CC4064756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/client.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type {\n ArticleDetail,\n ArticleResponse,\n ArticlesResponse,\n BookResponse,\n BooksResponse,\n ChapterResponse,\n ClientOptions,\n ListArticlesOptions,\n ListBooksOptions,\n ListFollowersOptions,\n ListScrapsOptions,\n ListUsersCommentsOptions,\n PublicationMini,\n PublicationResponse,\n ScrapsResponse,\n ScrapResponse,\n SearchOptions,\n SearchResponse,\n SearchSource,\n TopicResponse,\n TopicsResponse,\n UserCommentsResponse,\n UserResponse,\n UsersResponse,\n Event,\n} from \"./types.ts\";\n\nconst DEFAULT_BASE_URL = \"https://zenn.dev\";\n\nexport class ZennApiError extends Error {\n status: number;\n body: unknown;\n\n constructor(status: number, body: unknown, message: string) {\n super(message);\n this.name = \"ZennApiError\";\n this.status = status;\n this.body = body;\n }\n}\n\nexport interface PublicationsMiniResponse {\n publications: PublicationMini[];\n next_page: number | null;\n}\n\nfunction buildQuery(params: object | undefined): string {\n if (!params) return \"\";\n const search = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v === undefined || v === null || v === \"\") continue;\n search.set(k, String(v));\n }\n const qs = search.toString();\n return qs.length > 0 ? `?${qs}` : \"\";\n}\n\nexport class ZennClient {\n private readonly baseUrl: string;\n private readonly fetcher: typeof fetch;\n private readonly headers: HeadersInit;\n\n constructor(options: ClientOptions = {}) {\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.fetcher = options.fetch ?? fetch;\n this.headers = options.headers ?? {};\n }\n\n private async request<T>(path: string, params?: object): Promise<T> {\n const url = `${this.baseUrl}${path}${buildQuery(params)}`;\n const res = await this.fetcher(url, {\n headers: {\n Accept: \"application/json\",\n ...this.headers,\n },\n });\n const text = await res.text();\n const data: unknown = text.length > 0 ? JSON.parse(text) : null;\n if (!res.ok) {\n throw new ZennApiError(res.status, data, `Zenn API request failed: ${res.status} ${res.statusText} (${url})`);\n }\n return data as T;\n }\n\n // ---------- Articles ----------\n\n listArticles(options: ListArticlesOptions = {}): Promise<ArticlesResponse> {\n return this.request<ArticlesResponse>(\"/api/articles\", options);\n }\n\n getArticle(slug: string): Promise<ArticleResponse> {\n return this.request<ArticleResponse>(`/api/articles/${encodeURIComponent(slug)}`);\n }\n\n getArticleById(id: number): Promise<ArticleResponse> {\n return this.request<ArticleResponse>(\"/api/articles\", { id });\n }\n\n async *iterArticles(options: Omit<ListArticlesOptions, \"page\"> = {}): AsyncGenerator<ArticleDetail, void, void> {\n let page = 1;\n while (true) {\n const res = await this.listArticles({ ...options, page });\n for (const item of res.articles) {\n const detail = await this.getArticle(item.slug);\n yield detail.article;\n }\n if (res.next_page === null || res.next_page === undefined) break;\n page = res.next_page;\n }\n }\n\n // ---------- Books ----------\n\n listBooks(options: ListBooksOptions = {}): Promise<BooksResponse> {\n return this.request<BooksResponse>(\"/api/books\", options);\n }\n\n getBook(slug: string): Promise<BookResponse> {\n return this.request<BookResponse>(`/api/books/${encodeURIComponent(slug)}`);\n }\n\n getChapter(id: number): Promise<ChapterResponse> {\n return this.request<ChapterResponse>(`/api/chapters/${id}`);\n }\n\n // ---------- Scraps ----------\n\n listScraps(options: ListScrapsOptions = {}): Promise<ScrapsResponse> {\n return this.request<ScrapsResponse>(\"/api/scraps\", options);\n }\n\n getScrap(slug: string): Promise<ScrapResponse> {\n return this.request<ScrapResponse>(`/api/scraps/${encodeURIComponent(slug)}`);\n }\n\n // ---------- Users ----------\n\n getUser(username: string): Promise<UserResponse> {\n return this.request<UserResponse>(`/api/users/${encodeURIComponent(username)}`);\n }\n\n getCurrentUser(): Promise<UserResponse> {\n return this.request<UserResponse>(\"/api/users/current\");\n }\n\n getUserFollowers(username: string, options: ListFollowersOptions = {}): Promise<UsersResponse> {\n return this.request<UsersResponse>(`/api/users/${encodeURIComponent(username)}/followers`, options);\n }\n\n getUserFollowingsUsers(username: string, options: ListFollowersOptions = {}): Promise<UsersResponse> {\n return this.request<UsersResponse>(`/api/users/${encodeURIComponent(username)}/followings/users`, options);\n }\n\n getUserFollowingsPublications(\n username: string,\n options: ListFollowersOptions = {},\n ): Promise<PublicationsMiniResponse> {\n return this.request<PublicationsMiniResponse>(\n `/api/users/${encodeURIComponent(username)}/followings/publications`,\n options,\n );\n }\n\n getUserComments(username: string, options: ListUsersCommentsOptions = {}): Promise<UserCommentsResponse> {\n return this.request<UserCommentsResponse>(`/api/users/${encodeURIComponent(username)}/comments`, options);\n }\n\n // ---------- Publications ----------\n\n getPublication(name: string): Promise<PublicationResponse> {\n return this.request<PublicationResponse>(`/api/publications/${encodeURIComponent(name)}`);\n }\n\n getPublicationFollowers(name: string, options: ListFollowersOptions = {}): Promise<UsersResponse> {\n return this.request<UsersResponse>(`/api/publications/${encodeURIComponent(name)}/followers`, options);\n }\n\n // ---------- Topics ----------\n\n listTopics(): Promise<TopicsResponse> {\n return this.request<TopicsResponse>(\"/api/topics\");\n }\n\n getTopic(name: string): Promise<TopicResponse> {\n return this.request<TopicResponse>(`/api/topics/${encodeURIComponent(name)}`);\n }\n\n // ---------- Search ----------\n\n search<TSource extends SearchSource>(\n options: SearchOptions & { source: TSource },\n ): Promise<SearchResponse<TSource>> {\n return this.request<SearchResponse<TSource>>(\"/api/search\", options);\n }\n\n // ---------- Events ----------\n\n listEvents(): Promise<Event[]> {\n return this.request<Event[]>(\"/api/events\");\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "5FC52C405406CC4064756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the public Zenn API (https://zenn.dev/api/*).
|
|
3
|
+
*
|
|
4
|
+
* All payloads are returned as snake_case keys from the server, so the
|
|
5
|
+
* types below mirror that wire format. Optional fields are flagged with
|
|
6
|
+
* `?` only when the server has been observed to omit them.
|
|
7
|
+
*/
|
|
8
|
+
export type Iso8601 = string;
|
|
9
|
+
export type ArticleType = "tech" | "idea";
|
|
10
|
+
export type PostType = "Article" | "Book" | "Scrap" | "Comment" | "Chapter";
|
|
11
|
+
export type PrincipalType = "User" | "Publication";
|
|
12
|
+
export type CommentableType = "Article" | "Scrap";
|
|
13
|
+
export type EventType = "contest" | "hackathon" | "event";
|
|
14
|
+
export type PrizeRank = "grand_prize" | "winner" | "runner_up" | "participant" | (string & {});
|
|
15
|
+
export interface UserMini {
|
|
16
|
+
id: number;
|
|
17
|
+
username: string;
|
|
18
|
+
name: string;
|
|
19
|
+
avatar_small_url: string;
|
|
20
|
+
avatar_url?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface PublicationMini {
|
|
23
|
+
id: number;
|
|
24
|
+
name: string;
|
|
25
|
+
display_name: string;
|
|
26
|
+
avatar_small_url: string;
|
|
27
|
+
avatar_url: string;
|
|
28
|
+
pro: boolean;
|
|
29
|
+
avatar_registered: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface PublicationArticleOverride {
|
|
32
|
+
publication_id: number;
|
|
33
|
+
pathname: string;
|
|
34
|
+
title: string;
|
|
35
|
+
author_type: "Publication" | "User";
|
|
36
|
+
publication_avatar_registered?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface Topic {
|
|
39
|
+
id: number;
|
|
40
|
+
name: string;
|
|
41
|
+
taggings_count: number;
|
|
42
|
+
image_url: string;
|
|
43
|
+
display_name: string;
|
|
44
|
+
}
|
|
45
|
+
export interface TocItem {
|
|
46
|
+
id: string;
|
|
47
|
+
text: string;
|
|
48
|
+
level: number;
|
|
49
|
+
children: TocItem[];
|
|
50
|
+
}
|
|
51
|
+
export interface UserAward {
|
|
52
|
+
event_slug: string;
|
|
53
|
+
event_title: string;
|
|
54
|
+
event_type: EventType;
|
|
55
|
+
prize_rank: PrizeRank;
|
|
56
|
+
prize_display_name: string;
|
|
57
|
+
article: ArticleListItem;
|
|
58
|
+
}
|
|
59
|
+
export interface User {
|
|
60
|
+
id: number;
|
|
61
|
+
username: string;
|
|
62
|
+
name: string;
|
|
63
|
+
avatar_small_url: string;
|
|
64
|
+
avatar_url: string;
|
|
65
|
+
bio: string;
|
|
66
|
+
autolinked_bio: string;
|
|
67
|
+
github_username: string | null;
|
|
68
|
+
twitter_username: string | null;
|
|
69
|
+
is_support_open: boolean;
|
|
70
|
+
tokusyo_contact: string | null;
|
|
71
|
+
tokusyo_name: string | null;
|
|
72
|
+
website_url: string | null;
|
|
73
|
+
website_domain: string | null;
|
|
74
|
+
total_liked_count: number;
|
|
75
|
+
ga_tracking_id: string | null;
|
|
76
|
+
hatena_id: string | null;
|
|
77
|
+
is_invoice_issuer: boolean;
|
|
78
|
+
follower_count: number;
|
|
79
|
+
following_count: number;
|
|
80
|
+
following_user_count: number;
|
|
81
|
+
following_publication_count: number;
|
|
82
|
+
badge_count: number;
|
|
83
|
+
articles_count: number;
|
|
84
|
+
books_count: number;
|
|
85
|
+
scraps_count: number;
|
|
86
|
+
awards: UserAward[];
|
|
87
|
+
}
|
|
88
|
+
export interface PublicationNavigation {
|
|
89
|
+
id: number;
|
|
90
|
+
position: number;
|
|
91
|
+
label: string;
|
|
92
|
+
url: string;
|
|
93
|
+
}
|
|
94
|
+
export interface PublicationArticleList {
|
|
95
|
+
id: number;
|
|
96
|
+
name: string;
|
|
97
|
+
slug: string;
|
|
98
|
+
description: string;
|
|
99
|
+
articles_count: number;
|
|
100
|
+
position: number;
|
|
101
|
+
}
|
|
102
|
+
export interface Publication {
|
|
103
|
+
id: number;
|
|
104
|
+
name: string;
|
|
105
|
+
display_name: string;
|
|
106
|
+
avatar_small_url: string;
|
|
107
|
+
avatar_url: string;
|
|
108
|
+
pro: boolean;
|
|
109
|
+
avatar_registered: boolean;
|
|
110
|
+
description: string;
|
|
111
|
+
autolinked_description: string;
|
|
112
|
+
twitter_username: string | null;
|
|
113
|
+
github_username: string | null;
|
|
114
|
+
cover_image_url: string;
|
|
115
|
+
fixed_sentences_html: string;
|
|
116
|
+
is_support_open: boolean;
|
|
117
|
+
is_article_comment_open: boolean;
|
|
118
|
+
ga_tracking_id: string | null;
|
|
119
|
+
follower_count: number;
|
|
120
|
+
sponsor_count: number;
|
|
121
|
+
navigations: PublicationNavigation[];
|
|
122
|
+
article_lists: PublicationArticleList[];
|
|
123
|
+
has_recent_article_lists: boolean;
|
|
124
|
+
}
|
|
125
|
+
export interface GitHubRepository {
|
|
126
|
+
id: number;
|
|
127
|
+
owner_name: string;
|
|
128
|
+
repo_name: string;
|
|
129
|
+
}
|
|
130
|
+
export interface ArticleListItem {
|
|
131
|
+
id: number;
|
|
132
|
+
post_type: "Article";
|
|
133
|
+
slug: string;
|
|
134
|
+
comments_count: number;
|
|
135
|
+
liked_count: number;
|
|
136
|
+
bookmarked_count: number;
|
|
137
|
+
body_letters_count: number;
|
|
138
|
+
article_type: ArticleType;
|
|
139
|
+
emoji: string;
|
|
140
|
+
is_suspending_private: boolean;
|
|
141
|
+
published_at: Iso8601;
|
|
142
|
+
body_updated_at: Iso8601;
|
|
143
|
+
source_repo_updated_at: Iso8601 | null;
|
|
144
|
+
pinned: boolean;
|
|
145
|
+
path: string;
|
|
146
|
+
principal_type: PrincipalType;
|
|
147
|
+
title: string;
|
|
148
|
+
user: UserMini;
|
|
149
|
+
publication: PublicationMini | null;
|
|
150
|
+
publication_article_override: PublicationArticleOverride | null;
|
|
151
|
+
}
|
|
152
|
+
export interface ArticlesResponse {
|
|
153
|
+
articles: ArticleListItem[];
|
|
154
|
+
next_page: number | null;
|
|
155
|
+
total_count?: number;
|
|
156
|
+
}
|
|
157
|
+
export interface ArticleDetail extends ArticleListItem {
|
|
158
|
+
toc_enabled: boolean;
|
|
159
|
+
should_noindex: boolean;
|
|
160
|
+
scheduled_publish_at: Iso8601 | null;
|
|
161
|
+
can_send_badge: boolean;
|
|
162
|
+
status: "published" | "draft" | (string & {});
|
|
163
|
+
og_image_url: string;
|
|
164
|
+
body_html: string;
|
|
165
|
+
toc: TocItem[];
|
|
166
|
+
badges: ArticleBadge[];
|
|
167
|
+
is_mine: boolean;
|
|
168
|
+
is_preview: boolean;
|
|
169
|
+
draft_reveal_scope: string | null;
|
|
170
|
+
current_user_liked: boolean;
|
|
171
|
+
current_user_bookmarked: boolean;
|
|
172
|
+
github_repository: GitHubRepository | null;
|
|
173
|
+
publication_article_override: PublicationArticleOverride | null;
|
|
174
|
+
contest: ArticleContest | null;
|
|
175
|
+
topics: Topic[];
|
|
176
|
+
top_article_list: unknown;
|
|
177
|
+
comments: ArticleComment[];
|
|
178
|
+
positive_comments_count: number;
|
|
179
|
+
commented_users: UserMini[];
|
|
180
|
+
}
|
|
181
|
+
export interface ArticleResponse {
|
|
182
|
+
article: ArticleDetail;
|
|
183
|
+
}
|
|
184
|
+
export interface ArticleBadge {
|
|
185
|
+
badge_type: string;
|
|
186
|
+
rank: string | null;
|
|
187
|
+
created_at: Iso8601;
|
|
188
|
+
}
|
|
189
|
+
export interface ArticleContest {
|
|
190
|
+
slug: string;
|
|
191
|
+
title: string;
|
|
192
|
+
emoji: string;
|
|
193
|
+
icon_url: string;
|
|
194
|
+
description: string;
|
|
195
|
+
short_title: string;
|
|
196
|
+
url: string;
|
|
197
|
+
started_at: Iso8601;
|
|
198
|
+
ended_at: Iso8601;
|
|
199
|
+
}
|
|
200
|
+
export interface BookListItem {
|
|
201
|
+
id: number;
|
|
202
|
+
post_type: "Book";
|
|
203
|
+
title: string;
|
|
204
|
+
slug: string;
|
|
205
|
+
published: boolean;
|
|
206
|
+
price: number;
|
|
207
|
+
is_suspending_private: boolean;
|
|
208
|
+
liked_count: number;
|
|
209
|
+
published_at: Iso8601;
|
|
210
|
+
body_updated_at: Iso8601;
|
|
211
|
+
source_repo_updated_at: Iso8601;
|
|
212
|
+
cover_image_small_url: string;
|
|
213
|
+
path: string;
|
|
214
|
+
user: UserMini;
|
|
215
|
+
}
|
|
216
|
+
export interface BooksResponse {
|
|
217
|
+
books: BookListItem[];
|
|
218
|
+
next_page: number | null;
|
|
219
|
+
}
|
|
220
|
+
export interface BookChapter {
|
|
221
|
+
id: number;
|
|
222
|
+
post_type: "Chapter";
|
|
223
|
+
is_free: boolean;
|
|
224
|
+
slug: string;
|
|
225
|
+
body_letters_count: number;
|
|
226
|
+
source_repo_updated_at: Iso8601;
|
|
227
|
+
body_updated_at: Iso8601;
|
|
228
|
+
filename: string;
|
|
229
|
+
position: number;
|
|
230
|
+
path: string;
|
|
231
|
+
title: string;
|
|
232
|
+
}
|
|
233
|
+
export interface BookBadge {
|
|
234
|
+
badge_type: string;
|
|
235
|
+
rank: string | null;
|
|
236
|
+
created_at: Iso8601;
|
|
237
|
+
}
|
|
238
|
+
export interface BookContact {
|
|
239
|
+
url: string;
|
|
240
|
+
label: string;
|
|
241
|
+
}
|
|
242
|
+
export interface BookDetail {
|
|
243
|
+
id: number;
|
|
244
|
+
post_type: "Book";
|
|
245
|
+
title: string;
|
|
246
|
+
slug: string;
|
|
247
|
+
published: boolean;
|
|
248
|
+
price: number;
|
|
249
|
+
is_suspending_private: boolean;
|
|
250
|
+
liked_count: number;
|
|
251
|
+
published_at: Iso8601;
|
|
252
|
+
body_updated_at: Iso8601;
|
|
253
|
+
source_repo_updated_at: Iso8601;
|
|
254
|
+
cover_image_small_url: string;
|
|
255
|
+
path: string;
|
|
256
|
+
summary: string;
|
|
257
|
+
autolinked_summary: string;
|
|
258
|
+
og_image_url: string;
|
|
259
|
+
toc_enabled: boolean;
|
|
260
|
+
cover_image_url: string;
|
|
261
|
+
should_noindex: boolean;
|
|
262
|
+
total_letters_count: number;
|
|
263
|
+
badges: BookBadge[];
|
|
264
|
+
toc_depth: number;
|
|
265
|
+
require_additional_settings: boolean;
|
|
266
|
+
is_mine: boolean;
|
|
267
|
+
is_preview: boolean;
|
|
268
|
+
draft_reveal_scope: string | null;
|
|
269
|
+
current_user_liked: boolean;
|
|
270
|
+
purchase_in_progress: boolean;
|
|
271
|
+
can_read_all_chapters: boolean;
|
|
272
|
+
contact: BookContact | null;
|
|
273
|
+
user: User;
|
|
274
|
+
topics: Topic[];
|
|
275
|
+
chapters: BookChapter[];
|
|
276
|
+
github_repository: GitHubRepository | null;
|
|
277
|
+
cross_references: unknown[];
|
|
278
|
+
}
|
|
279
|
+
export interface BookResponse {
|
|
280
|
+
book: BookDetail;
|
|
281
|
+
}
|
|
282
|
+
export interface ChapterDetail {
|
|
283
|
+
id: number;
|
|
284
|
+
post_type: "Chapter";
|
|
285
|
+
is_free: boolean;
|
|
286
|
+
slug: string;
|
|
287
|
+
body_letters_count: number;
|
|
288
|
+
source_repo_updated_at: Iso8601;
|
|
289
|
+
body_updated_at: Iso8601;
|
|
290
|
+
filename: string;
|
|
291
|
+
position: number;
|
|
292
|
+
path: string;
|
|
293
|
+
title: string;
|
|
294
|
+
body_html: string;
|
|
295
|
+
toc: TocItem[];
|
|
296
|
+
}
|
|
297
|
+
export interface ChapterResponse {
|
|
298
|
+
chapter: ChapterDetail;
|
|
299
|
+
}
|
|
300
|
+
export interface ScrapListItem {
|
|
301
|
+
id: number;
|
|
302
|
+
post_type: "Scrap";
|
|
303
|
+
user_id: number;
|
|
304
|
+
slug: string;
|
|
305
|
+
title: string;
|
|
306
|
+
closed: boolean;
|
|
307
|
+
closed_at: Iso8601 | null;
|
|
308
|
+
archived: boolean;
|
|
309
|
+
liked_count: number;
|
|
310
|
+
can_others_post: boolean;
|
|
311
|
+
comments_count: number;
|
|
312
|
+
created_at: Iso8601;
|
|
313
|
+
last_comment_created_at: Iso8601;
|
|
314
|
+
should_noindex: boolean;
|
|
315
|
+
path: string;
|
|
316
|
+
unlisted: boolean;
|
|
317
|
+
topics: Topic[];
|
|
318
|
+
user: UserMini;
|
|
319
|
+
}
|
|
320
|
+
export interface ScrapsResponse {
|
|
321
|
+
scraps: ScrapListItem[];
|
|
322
|
+
next_page: number | null;
|
|
323
|
+
}
|
|
324
|
+
export interface ScrapCommentable {
|
|
325
|
+
id: number;
|
|
326
|
+
post_type: "Scrap" | "Article";
|
|
327
|
+
user_id: number;
|
|
328
|
+
slug: string;
|
|
329
|
+
title: string;
|
|
330
|
+
closed: boolean;
|
|
331
|
+
closed_at: Iso8601 | null;
|
|
332
|
+
archived: boolean;
|
|
333
|
+
liked_count: number;
|
|
334
|
+
can_others_post: boolean;
|
|
335
|
+
comments_count: number;
|
|
336
|
+
created_at: Iso8601;
|
|
337
|
+
last_comment_created_at: Iso8601;
|
|
338
|
+
should_noindex: boolean;
|
|
339
|
+
path: string;
|
|
340
|
+
unlisted: boolean;
|
|
341
|
+
}
|
|
342
|
+
export interface ScrapDetail extends ScrapListItem {
|
|
343
|
+
current_user_liked: boolean;
|
|
344
|
+
is_mine: boolean;
|
|
345
|
+
comments: ArticleComment[];
|
|
346
|
+
positive_comments_count: number;
|
|
347
|
+
commented_users: UserMini[];
|
|
348
|
+
}
|
|
349
|
+
export interface ScrapResponse {
|
|
350
|
+
scrap: ScrapDetail;
|
|
351
|
+
}
|
|
352
|
+
export interface ArticleComment {
|
|
353
|
+
id: number;
|
|
354
|
+
post_type: "Comment";
|
|
355
|
+
slug: string;
|
|
356
|
+
user_id: number;
|
|
357
|
+
liked_count: number;
|
|
358
|
+
body_updated_at: Iso8601 | null;
|
|
359
|
+
created_at: Iso8601;
|
|
360
|
+
pinned: boolean;
|
|
361
|
+
body_html: string;
|
|
362
|
+
hidden_reason: string | null;
|
|
363
|
+
current_user_liked: boolean;
|
|
364
|
+
is_mine: boolean;
|
|
365
|
+
user: UserMini;
|
|
366
|
+
children: ArticleComment[];
|
|
367
|
+
}
|
|
368
|
+
export interface UserCommentItem {
|
|
369
|
+
id: number;
|
|
370
|
+
post_type: "Comment";
|
|
371
|
+
parent_id: number | null;
|
|
372
|
+
slug: string;
|
|
373
|
+
commentable_type: CommentableType;
|
|
374
|
+
user_id: number;
|
|
375
|
+
liked_count: number;
|
|
376
|
+
body_updated_at: Iso8601 | null;
|
|
377
|
+
created_at: Iso8601;
|
|
378
|
+
reply: boolean;
|
|
379
|
+
title: string;
|
|
380
|
+
commentable: ScrapCommentable;
|
|
381
|
+
}
|
|
382
|
+
export interface UserCommentsResponse {
|
|
383
|
+
comments: UserCommentItem[];
|
|
384
|
+
next_page: number | null;
|
|
385
|
+
}
|
|
386
|
+
export interface UsersResponse {
|
|
387
|
+
users: UserMini[];
|
|
388
|
+
next_page: number | null;
|
|
389
|
+
}
|
|
390
|
+
export interface UserResponse {
|
|
391
|
+
user: User;
|
|
392
|
+
}
|
|
393
|
+
export interface PublicationResponse {
|
|
394
|
+
publication: Publication;
|
|
395
|
+
}
|
|
396
|
+
export interface TopicsResponse {
|
|
397
|
+
topics: Topic[];
|
|
398
|
+
}
|
|
399
|
+
export interface TopicResponse {
|
|
400
|
+
topic: Topic;
|
|
401
|
+
}
|
|
402
|
+
export interface Event {
|
|
403
|
+
slug: string;
|
|
404
|
+
event_type: EventType;
|
|
405
|
+
emoji: string;
|
|
406
|
+
icon_url: string;
|
|
407
|
+
title: string;
|
|
408
|
+
description: string;
|
|
409
|
+
short_title: string;
|
|
410
|
+
url: string;
|
|
411
|
+
target_blank: boolean;
|
|
412
|
+
promotion_started_at: Iso8601;
|
|
413
|
+
promotion_ended_at: Iso8601;
|
|
414
|
+
started_at: Iso8601;
|
|
415
|
+
ended_at: Iso8601;
|
|
416
|
+
}
|
|
417
|
+
export type SearchSource = "articles" | "users" | "topics" | "publications" | "scraps" | "books";
|
|
418
|
+
export interface SearchResponse<TKey extends SearchSource> {
|
|
419
|
+
articles?: ArticleListItem[];
|
|
420
|
+
users?: UserMini[];
|
|
421
|
+
topics?: Topic[];
|
|
422
|
+
publications?: PublicationMini[];
|
|
423
|
+
scraps?: ScrapListItem[];
|
|
424
|
+
books?: BookListItem[];
|
|
425
|
+
next_page: number | null;
|
|
426
|
+
}
|
|
427
|
+
export type ArticleListOrder = "latest" | "liked" | "trending" | "new" | "weekly_liked" | "monthly_liked" | "liked_daily" | "liked_weekly" | "liked_monthly" | "liked_alltime" | "daily_tech" | "daily_idea" | "daily_books" | "weekly_tech" | "weekly_idea" | "alltime_tech" | "alltime_idea" | "weekly_liked_tech" | "monthly_liked_tech" | "alltime_liked_tech" | (string & {});
|
|
428
|
+
export type BookListOrder = ArticleListOrder;
|
|
429
|
+
export type ScrapListOrder = ArticleListOrder;
|
|
430
|
+
export interface ListArticlesOptions {
|
|
431
|
+
order?: ArticleListOrder;
|
|
432
|
+
page?: number;
|
|
433
|
+
username?: string;
|
|
434
|
+
publication_name?: string;
|
|
435
|
+
topicname?: string;
|
|
436
|
+
article_type?: ArticleType;
|
|
437
|
+
}
|
|
438
|
+
export interface ListBooksOptions {
|
|
439
|
+
order?: BookListOrder;
|
|
440
|
+
page?: number;
|
|
441
|
+
username?: string;
|
|
442
|
+
publication_name?: string;
|
|
443
|
+
topicname?: string;
|
|
444
|
+
}
|
|
445
|
+
export interface ListScrapsOptions {
|
|
446
|
+
order?: ScrapListOrder;
|
|
447
|
+
page?: number;
|
|
448
|
+
username?: string;
|
|
449
|
+
publication_name?: string;
|
|
450
|
+
topicname?: string;
|
|
451
|
+
}
|
|
452
|
+
export interface ListUsersCommentsOptions {
|
|
453
|
+
page?: number;
|
|
454
|
+
commentable_type?: CommentableType;
|
|
455
|
+
}
|
|
456
|
+
export interface ListFollowersOptions {
|
|
457
|
+
page?: number;
|
|
458
|
+
}
|
|
459
|
+
export interface SearchOptions {
|
|
460
|
+
q: string;
|
|
461
|
+
source: SearchSource;
|
|
462
|
+
page?: number;
|
|
463
|
+
}
|
|
464
|
+
export interface ClientOptions {
|
|
465
|
+
baseUrl?: string;
|
|
466
|
+
fetch?: typeof fetch;
|
|
467
|
+
headers?: HeadersInit;
|
|
468
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@taisan11/zenn.js",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Typed client for the public Zenn API.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"./package.json": "./package.json"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "bun run scripts/build.ts",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"prepublishOnly": "bun run build"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/bun": "latest",
|
|
33
|
+
"typescript": "^5.9.3"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"typescript": "^5"
|
|
37
|
+
}
|
|
38
|
+
}
|