From a75577d87f300d1bfa57590af24c0d2ae8c6090d Mon Sep 17 00:00:00 2001 From: takker99 <37929109+takker99@users.noreply.github.com> Date: Fri, 25 Mar 2022 01:42:43 +0900 Subject: [PATCH 1/4] =?UTF-8?q?:boom:=20:sparkles:=20=E3=81=84=E3=81=8F?= =?UTF-8?q?=E3=81=A4=E3=81=8B=E3=81=AEoption=E8=BF=BD=E5=8A=A0=E3=81=A8?= =?UTF-8?q?=E3=80=81Error=E5=91=A8=E3=82=8A=E3=82=92=E3=81=8D=E3=82=8C?= =?UTF-8?q?=E3=81=84=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 想定外のエラーをErrorを継承したclassで投げる - fetchをカスタマイズできる - host nameを変えられる --- is.ts | 26 ++++++++++++++ rest/auth.ts | 28 +++++++++++++++ rest/error.ts | 25 +++++++++++++ rest/page-data.ts | 86 +++++++++++++++++--------------------------- rest/pages.ts | 85 +++++++++++++++++++------------------------ rest/profile.ts | 30 +++++++--------- rest/project.ts | 49 +++++++++++-------------- rest/replaceLinks.ts | 56 ++++++++++------------------- rest/util.ts | 51 ++++++++++++++++++++++++++ rest/utils.ts | 61 ------------------------------- 10 files changed, 249 insertions(+), 248 deletions(-) create mode 100644 rest/auth.ts create mode 100644 rest/error.ts create mode 100644 rest/util.ts delete mode 100644 rest/utils.ts diff --git a/is.ts b/is.ts index 574afcd..d32b173 100644 --- a/is.ts +++ b/is.ts @@ -1,3 +1,4 @@ +import type { ErrorLike } from "./deps/scrapbox.ts"; // These code are based on https://deno.land/x/unknownutil@v1.1.0/is.ts export const isNone = (value: unknown): value is undefined | null => @@ -8,3 +9,28 @@ export const isNumber = (value: unknown): value is number => typeof value === "number"; export const isArray = (value: unknown): value is T[] => Array.isArray(value); +export const isObject = (value: unknown): value is Record => + typeof value === "object" && value !== null; + +export const isErrorLike = (e: unknown): e is ErrorLike => { + if (!isObject(e)) return false; + return (e.name === undefined || typeof e.name === "string") && + typeof e.message === "string"; +}; + +/** 与えられたobjectもしくはJSONテキストをErrorLikeに変換できるかどうか試す + * + * @param e 試したいobjectもしくはテキスト + * @return 変換できなかったら`false`を返す。変換できたらそのobjectを返す + */ +export const tryToErrorLike = (e: unknown): false | ErrorLike => { + try { + const json = typeof e === "string" ? JSON.parse(e) : e; + if (!isErrorLike(json)) return false; + return json; + } catch (e2: unknown) { + if (e2 instanceof SyntaxError) return false; + // JSONのparse error以外はそのまま投げる + throw e2; + } +}; diff --git a/rest/auth.ts b/rest/auth.ts new file mode 100644 index 0000000..5b9a96a --- /dev/null +++ b/rest/auth.ts @@ -0,0 +1,28 @@ +import { getProfile } from "./profile.ts"; +import { BaseOptions } from "./util.ts"; + +// scrapbox.io内なら`window._csrf`にCSRF tokenが入っている +declare global { + interface Window { + _csrf?: string; + } +} + +/** HTTP headerのCookieに入れる文字列を作る + * + * @param sid connect.sidに入っている文字列 + */ +export const cookie = (sid: string) => `connect.sid=${sid}`; + +/** CSRF tokenを取得する + * + * @param init 認証情報など + */ +export const getCSRFToken = async ( + init?: BaseOptions, +): Promise => { + if (window._csrf) return window._csrf; + + const user = await getProfile(init); + return user.csrfToken; +}; diff --git a/rest/error.ts b/rest/error.ts new file mode 100644 index 0000000..450250f --- /dev/null +++ b/rest/error.ts @@ -0,0 +1,25 @@ +export class UnexpectedResponseError extends Error { + name = "UnexpectedResponseError"; + status: number; + statusText: string; + body: string; + path: URL; + + constructor( + init: { status: number; statusText: string; body: string; path: URL }, + ) { + super( + `${init.status} ${init.statusText} when fetching ${init.path.toString()}`, + ); + + this.status = init.status; + this.statusText = init.statusText; + this.body = init.body; + this.path = init.path; + + // @ts-ignore only available on V8 + if (Error.captureStackTrace) { + Error.captureStackTrace(this, UnexpectedResponseError); + } + } +} diff --git a/rest/page-data.ts b/rest/page-data.ts index e0bee80..40728ff 100644 --- a/rest/page-data.ts +++ b/rest/page-data.ts @@ -6,23 +6,10 @@ import type { NotLoggedInError, NotPrivilegeError, } from "../deps/scrapbox.ts"; -import { - cookie, - getCSRFToken, - makeCustomError, - tryToErrorLike, -} from "./utils.ts"; -import type { Result } from "./utils.ts"; - -/** `importPages`の認証情報 */ -export interface ImportInit { - /** connect.sid */ sid: string; - /** CSRF token - * - * If it isn't set, automatically get CSRF token from scrapbox.io server. - */ - csrf?: string; -} +import { cookie, getCSRFToken } from "./auth.ts"; +import { UnexpectedResponseError } from "./error.ts"; +import { tryToErrorLike } from "../is.ts"; +import { BaseOptions, ExtendedOptions, Result, setDefaults } from "./util.ts"; /** projectにページをインポートする * * @param project - インポート先のprojectの名前 @@ -31,7 +18,7 @@ export interface ImportInit { export async function importPages( project: string, data: ImportedData, - { sid, csrf }: ImportInit, + init: ExtendedOptions, ): Promise< Result > { @@ -39,6 +26,7 @@ export async function importPages( return { ok: true, value: "No pages to import." }; } + const { sid, hostName, fetch, csrf } = setDefaults(init ?? {}); const formData = new FormData(); formData.append( "import-file", @@ -47,43 +35,41 @@ export async function importPages( }), ); formData.append("name", "undefined"); + const path = `https://${hostName}/api/page-data/import/${project}.json`; - csrf ??= await getCSRFToken(sid); - - const path = `https://scrapbox.io/api/page-data/import/${project}.json`; const res = await fetch( path, { method: "POST", headers: { - Cookie: cookie(sid), + ...(sid ? { Cookie: cookie(sid) } : {}), Accept: "application/json, text/plain, */*", - "X-CSRF-TOKEN": csrf, + "X-CSRF-TOKEN": csrf ?? await getCSRFToken(init), }, body: formData, }, ); if (!res.ok) { - if (res.status === 503) { - throw makeCustomError("ServerError", "503 Service Unavailable"); - } - const value = tryToErrorLike(await res.text()); + const text = await res.json(); + const value = tryToErrorLike(text); if (!value) { - throw makeCustomError( - "UnexpectedError", - `Unexpected error has occuerd when fetching "${path}"`, - ); + throw new UnexpectedResponseError({ + path: new URL(path), + ...res, + body: await res.text(), + }); } return { ok: false, value }; } + const { message } = (await res.json()) as { message: string }; return { ok: true, value: message }; } /** `exportPages`の認証情報 */ -export interface ExportInit { - /** connect.sid */ sid: string; +export interface ExportInit + extends BaseOptions { /** whether to includes metadata */ metadata: withMetadata; } /** projectの全ページをエクスポートする @@ -92,45 +78,37 @@ export interface ExportInit { */ export async function exportPages( project: string, - { sid, metadata }: ExportInit, + init: ExportInit, ): Promise< Result< ExportedData, NotFoundError | NotPrivilegeError | NotLoggedInError > > { + const { sid, hostName, fetch, metadata } = setDefaults(init ?? {}); const path = - `https://scrapbox.io/api/page-data/export/${project}.json?metadata=${metadata}`; + `https://${hostName}/api/page-data/export/${project}.json?metadata=${metadata}`; const res = await fetch( path, - { - headers: { - Cookie: cookie(sid), - }, - }, + sid ? { headers: { Cookie: cookie(sid) } } : undefined, ); if (!res.ok) { - const error = (await res.json()); - return { ok: false, ...error }; - } - if (!res.ok) { - const value = tryToErrorLike(await res.text()) as - | false - | NotFoundError - | NotPrivilegeError - | NotLoggedInError; + const text = await res.json(); + const value = tryToErrorLike(text); if (!value) { - throw makeCustomError( - "UnexpectedError", - `Unexpected error has occuerd when fetching "${path}"`, - ); + throw new UnexpectedResponseError({ + path: new URL(path), + ...res, + body: await res.text(), + }); } return { ok: false, - value, + value: value as NotFoundError | NotPrivilegeError | NotLoggedInError, }; } + const value = (await res.json()) as ExportedData; return { ok: true, value }; } diff --git a/rest/pages.ts b/rest/pages.ts index 84a0c55..4010e4e 100644 --- a/rest/pages.ts +++ b/rest/pages.ts @@ -5,14 +5,15 @@ import type { Page, PageList, } from "../deps/scrapbox.ts"; -import { cookie, makeCustomError, tryToErrorLike } from "./utils.ts"; +import { cookie } from "./auth.ts"; +import { UnexpectedResponseError } from "./error.ts"; +import { tryToErrorLike } from "../is.ts"; import { encodeTitleURI } from "../title.ts"; -import type { Result } from "./utils.ts"; +import { BaseOptions, Result, setDefaults } from "./util.ts"; /** Options for `getPage()` */ -export interface GetPageOption { +export interface GetPageOption extends BaseOptions { /** use `followRename` */ followRename?: boolean; - /** connect.sid */ sid?: string; } /** 指定したページのJSONデータを取得する * @@ -30,36 +31,30 @@ export async function getPage( NotFoundError | NotLoggedInError | NotMemberError > > { - const path = `https://scrapbox.io/api/pages/${project}/${ + const { sid, hostName, fetch, followRename } = setDefaults(options ?? {}); + const path = `https://${hostName}/api/pages/${project}/${ encodeTitleURI(title) - }?followRename=${options?.followRename ?? true}`; - + }?followRename=${followRename ?? true}`; const res = await fetch( path, - options?.sid - ? { - headers: { - Cookie: cookie(options.sid), - }, - } - : undefined, + sid ? { headers: { Cookie: cookie(sid) } } : undefined, ); - if (!res.ok) { - const value = tryToErrorLike(await res.text()) as - | false - | NotFoundError - | NotLoggedInError - | NotMemberError; + const text = await res.text(); + const value = tryToErrorLike(text); if (!value) { - throw makeCustomError( - "UnexpectedError", - `Unexpected error has occuerd when fetching "${path}"`, - ); + throw new UnexpectedResponseError({ + path: new URL(path), + ...res, + body: text, + }); } return { ok: false, - value, + value: value as + | NotFoundError + | NotLoggedInError + | NotMemberError, }; } const value = (await res.json()) as Page; @@ -67,7 +62,7 @@ export async function getPage( } /** Options for `listPages()` */ -export interface ListPagesOption { +export interface ListPagesOption extends BaseOptions { /** the sort of page list to return * * @default "updated" @@ -91,8 +86,6 @@ export interface ListPagesOption { * @default 100 */ limit?: number; - /** connect.sid */ - sid?: string; } /** 指定したprojectのページを一覧する * @@ -108,39 +101,35 @@ export async function listPages( NotFoundError | NotLoggedInError | NotMemberError > > { - const { sort, limit, skip } = options ?? {}; + const { sid, hostName, fetch, sort, limit, skip } = setDefaults( + options ?? {}, + ); const params = new URLSearchParams(); if (sort !== undefined) params.append("sort", sort); if (limit !== undefined) params.append("limit", `${limit}`); if (skip !== undefined) params.append("skip", `${skip}`); - const path = `https://scrapbox.io/api/pages/${project}?${params.toString()}`; + const path = `https://${hostName}/api/pages/${project}?${params.toString()}`; const res = await fetch( path, - options?.sid - ? { - headers: { - Cookie: cookie(options.sid), - }, - } - : undefined, + sid ? { headers: { Cookie: cookie(sid) } } : undefined, ); - if (!res.ok) { - const value = tryToErrorLike(await res.text()) as - | false - | NotFoundError - | NotLoggedInError - | NotMemberError; + const text = await res.text(); + const value = tryToErrorLike(text); if (!value) { - throw makeCustomError( - "UnexpectedError", - `Unexpected error has occuerd when fetching "${path}"`, - ); + throw new UnexpectedResponseError({ + path: new URL(path), + ...res, + body: text, + }); } return { ok: false, - value, + value: value as + | NotFoundError + | NotLoggedInError + | NotMemberError, }; } const value = (await res.json()) as PageList; diff --git a/rest/profile.ts b/rest/profile.ts index 60b95fe..8634951 100644 --- a/rest/profile.ts +++ b/rest/profile.ts @@ -1,33 +1,27 @@ import type { GuestUser, MemberUser } from "../deps/scrapbox.ts"; -import { cookie, makeCustomError } from "./utils.ts"; +import { cookie } from "./auth.ts"; +import { BaseOptions, setDefaults } from "./util.ts"; +import { UnexpectedResponseError } from "./error.ts"; -export interface ProfileInit { - /** connect.sid */ sid: string; -} /** get user profile * * @param init connect.sid etc. */ export async function getProfile( - init?: ProfileInit, + init?: BaseOptions, ): Promise { - const path = "https://scrapbox.io/api/users/me"; + const { sid, hostName, fetch } = setDefaults(init ?? {}); + const path = `https://${hostName}/api/users/me`; const res = await fetch( path, - init?.sid - ? { - headers: { - Cookie: cookie(init.sid), - }, - } - : undefined, + sid ? { headers: { Cookie: cookie(sid) } } : undefined, ); - if (!res.ok) { - throw makeCustomError( - "UnexpectedError", - `Unexpected error has occuerd when fetching "${path}"`, - ); + throw new UnexpectedResponseError({ + path: new URL(path), + ...res, + body: await res.text(), + }); } return (await res.json()) as MemberUser | GuestUser; } diff --git a/rest/project.ts b/rest/project.ts index 8886e0a..ee3bee6 100644 --- a/rest/project.ts +++ b/rest/project.ts @@ -1,9 +1,3 @@ -export async function getProjectId(project: string) { - const res = await fetch(`https://scrapbox.io/api/projects/${project}`); - const json = (await res.json()) as MemberProject; - return json.id; -} - import type { MemberProject, NotFoundError, @@ -11,11 +5,11 @@ import type { NotMemberError, NotMemberProject, } from "../deps/scrapbox.ts"; -import { cookie, makeCustomError, Result, tryToErrorLike } from "./utils.ts"; +import { cookie } from "./auth.ts"; +import { UnexpectedResponseError } from "./error.ts"; +import { tryToErrorLike } from "../is.ts"; +import { BaseOptions, Result, setDefaults } from "./util.ts"; -export interface ProjectInit { - /** connect.sid */ sid: string; -} /** get the project information * * @param project project name to get @@ -23,39 +17,36 @@ export interface ProjectInit { */ export async function getProject( project: string, - init?: ProjectInit, + init?: BaseOptions, ): Promise< Result< MemberProject | NotMemberProject, NotFoundError | NotMemberError | NotLoggedInError > > { - const path = `https://scrapbox.io/api/projects/${project}`; + const { sid, hostName, fetch } = setDefaults(init ?? {}); + const path = `https://${hostName}/api/projects/${project}`; const res = await fetch( path, - init?.sid - ? { - headers: { - Cookie: cookie(init.sid), - }, - } - : undefined, + sid ? { headers: { Cookie: cookie(sid) } } : undefined, ); if (!res.ok) { - const value = tryToErrorLike(await res.json()) as - | false - | NotFoundError - | NotMemberError - | NotLoggedInError; + const text = await res.json(); + const value = tryToErrorLike(text); if (!value) { - throw makeCustomError( - "UnexpectedError", - `Unexpected error has occuerd when fetching "${path}"`, - ); + throw new UnexpectedResponseError({ + path: new URL(path), + ...res, + body: await res.text(), + }); } - return { ok: false, value }; + return { + ok: false, + value: value as NotFoundError | NotMemberError | NotLoggedInError, + }; } + const value = (await res.json()) as MemberProject | NotMemberProject; return { ok: true, value }; } diff --git a/rest/replaceLinks.ts b/rest/replaceLinks.ts index e8ead4a..f16e7ea 100644 --- a/rest/replaceLinks.ts +++ b/rest/replaceLinks.ts @@ -3,23 +3,10 @@ import type { NotLoggedInError, NotMemberError, } from "../deps/scrapbox.ts"; -import { - cookie, - getCSRFToken, - makeCustomError, - tryToErrorLike, -} from "./utils.ts"; -import type { Result } from "./utils.ts"; - -/** `replaceLinks`の認証情報 */ -export interface ReplaceLinksInit { - /** connect.sid */ sid: string; - /** CSRF token - * - * If it isn't set, automatically get CSRF token from scrapbox.io server. - */ - csrf?: string; -} +import { cookie, getCSRFToken } from "./auth.ts"; +import { UnexpectedResponseError } from "./error.ts"; +import { tryToErrorLike } from "../is.ts"; +import { ExtendedOptions, Result, setDefaults } from "./util.ts"; /** 指定したproject内の全てのリンクを書き換える * @@ -29,23 +16,22 @@ export interface ReplaceLinksInit { * @param project これで指定したproject内の全てのリンクが置換対象となる * @param from 置換前のリンク * @param to 置換後のリンク - * @param options connect.sidなど + * @param init connect.sidなど * @return 置換されたリンクがあったページの数 */ export async function replaceLinks( project: string, from: string, to: string, - init?: ReplaceLinksInit, + init?: ExtendedOptions, ): Promise< Result< number, NotFoundError | NotLoggedInError | NotMemberError > > { - const path = `https://scrapbox.io/api/pages/${project}/replace/links`; - const sid = init?.sid; - const csrf = init?.csrf ?? await getCSRFToken(sid); + const { sid, hostName, fetch, csrf } = setDefaults(init ?? {}); + const path = `https://${hostName}/api/pages/${project}/replace/links`; const res = await fetch( path, @@ -53,32 +39,26 @@ export async function replaceLinks( method: "POST", headers: { "Content-Type": "application/json;charset=utf-8", - "X-CSRF-TOKEN": csrf, - ...(sid - ? { - Cookie: cookie(sid), - } - : {}), + "X-CSRF-TOKEN": csrf ?? await getCSRFToken(init), + ...(sid ? { Cookie: cookie(sid) } : {}), }, body: JSON.stringify({ from, to }), }, ); if (!res.ok) { - const value = tryToErrorLike(await res.text()) as - | false - | NotFoundError - | NotLoggedInError - | NotMemberError; + const text = await res.json(); + const value = tryToErrorLike(text); if (!value) { - throw makeCustomError( - "UnexpectedError", - `Unexpected error has occuerd when fetching "${path}"`, - ); + throw new UnexpectedResponseError({ + path: new URL(path), + ...res, + body: await res.text(), + }); } return { ok: false, - value, + value: value as NotFoundError | NotMemberError | NotLoggedInError, }; } diff --git a/rest/util.ts b/rest/util.ts new file mode 100644 index 0000000..2cd2a2a --- /dev/null +++ b/rest/util.ts @@ -0,0 +1,51 @@ +/** 正常値と異常値を格納する型 */ +export type Result = { ok: true; value: T } | { ok: false; value: E }; + +/** networkからdataをとってくる処理 + * + * interfaceは`fetch()`と同じ + */ +export type Fetch = ( + input: string | Request, + init?: RequestInit, +) => Promise; + +/** 全てのREST APIに共通するopitons */ +export interface BaseOptions { + /** connect.sid + * + * private projectのデータやscrapbox accountに紐付いたデータを取得する際に必要な認証情報 + */ + sid?: string; + + /** データの取得に使う処理 + * + * @default fetch + */ + fetch?: Fetch; + + /** REST APIのdomain + * + * オンプレ版scrapboxなどだと、scrapbox.io以外のhost nameになるので、予め変えられるようにしておく + * + * @default "scrapbox.io" + */ + hostName?: string; +} +/** BaseeOptionsにCSRF情報を入れたもの */ +export interface ExtendedOptions extends BaseOptions { + /** CSRF token + * + * If it isn't set, automatically get CSRF token from scrapbox.io server. + */ + csrf?: string; +} + +/** BaseOptionsの既定値を埋める */ +export const setDefaults = ( + options: T, +): Omit & Required> => { + const { fetch = globalThis.fetch, hostName = "scrapbox.io", ...rest } = + options; + return { fetch, hostName, ...rest }; +}; diff --git a/rest/utils.ts b/rest/utils.ts deleted file mode 100644 index c0c5ce6..0000000 --- a/rest/utils.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { ErrorLike } from "../deps/scrapbox.ts"; -import { getProfile } from "./profile.ts"; - -// scrapbox.io内なら`window._csrf`にCSRF tokenが入っている -declare global { - interface Window { - _csrf?: string; - } -} - -/** HTTP headerのCookieに入れる文字列を作る - * - * @param sid connect.sidに入っている文字列 - */ -export const cookie = (sid: string) => `connect.sid=${sid}`; - -export type Result = { ok: true; value: T } | { ok: false; value: E }; -/** CSRF tokenを取得する - * - * @param sid - connect.sidに入っている文字列。不正な文字列を入れてもCSRF tokenを取得できるみたい - */ -export async function getCSRFToken( - sid?: string, -): Promise { - if (window._csrf) return window._csrf; - - const user = await getProfile(sid ? { sid } : undefined); - return user.csrfToken; -} - -// cf. https://blog.uhy.ooo/entry/2021-04-09/typescript-is-any-as/#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E5%AE%9A%E7%BE%A9%E5%9E%8B%E3%82%AC%E3%83%BC%E3%83%89%E3%81%AE%E5%BC%95%E6%95%B0%E3%81%AE%E5%9E%8B%E3%82%92%E3%81%A9%E3%81%86%E3%81%99%E3%82%8B%E3%81%8B -function isNotNullish(data: unknown): data is Record { - return data != null; -} -function isErrorLike(e: unknown): e is ErrorLike { - if (!isNotNullish(e)) return false; - return (e.name === undefined || typeof e.name === "string") && - typeof e.message === "string"; -} -/** 与えられたobjectもしくはJSONテキストをErrorLikeに変換できるかどうか試す - * - * @param e 試したいobjectもしくはテキスト - */ -export function tryToErrorLike(e: unknown): false | ErrorLike { - try { - const json = typeof e === "string" ? JSON.parse(e) : e; - if (!isErrorLike(json)) return false; - return json; - } catch (e2: unknown) { - if (e2 instanceof SyntaxError) return false; - throw e2; - } -} - -/** classを使わずにカスタムエラーを作る */ -export function makeCustomError(name: string, message: string) { - const error = new Error(); - error.name = name; - error.message = message; - return error; -} From 49f0423e18ce80553c539829efc3405af0190bf5 Mon Sep 17 00:00:00 2001 From: takker99 <37929109+takker99@users.noreply.github.com> Date: Fri, 25 Mar 2022 01:48:58 +0900 Subject: [PATCH 2/4] :+1: re-export from mod.ts --- rest/mod.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rest/mod.ts b/rest/mod.ts index 708592a..9a45976 100644 --- a/rest/mod.ts +++ b/rest/mod.ts @@ -3,3 +3,6 @@ export * from "./project.ts"; export * from "./profile.ts"; export * from "./replaceLinks.ts"; export * from "./page-data.ts"; +export * from "./auth.ts"; +export * from "./util.ts"; +export * from "./error.ts"; From 519887ed6543316a51937b34ce776df14a3456b8 Mon Sep 17 00:00:00 2001 From: takker99 <37929109+takker99@users.noreply.github.com> Date: Fri, 25 Mar 2022 01:51:08 +0900 Subject: [PATCH 3/4] :recycle: Convert functions to arrow functions --- rest/auth.ts | 2 +- rest/page-data.ts | 12 ++++++------ rest/pages.ts | 12 ++++++------ rest/profile.ts | 6 +++--- rest/project.ts | 6 +++--- rest/replaceLinks.ts | 6 +++--- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/rest/auth.ts b/rest/auth.ts index 5b9a96a..3fa37d1 100644 --- a/rest/auth.ts +++ b/rest/auth.ts @@ -12,7 +12,7 @@ declare global { * * @param sid connect.sidに入っている文字列 */ -export const cookie = (sid: string) => `connect.sid=${sid}`; +export const cookie = (sid: string): string => `connect.sid=${sid}`; /** CSRF tokenを取得する * diff --git a/rest/page-data.ts b/rest/page-data.ts index 40728ff..57a9f27 100644 --- a/rest/page-data.ts +++ b/rest/page-data.ts @@ -15,13 +15,13 @@ import { BaseOptions, ExtendedOptions, Result, setDefaults } from "./util.ts"; * @param project - インポート先のprojectの名前 * @param data - インポートするページデータ */ -export async function importPages( +export const importPages = async ( project: string, data: ImportedData, init: ExtendedOptions, ): Promise< Result -> { +> => { if (data.pages.length === 0) { return { ok: true, value: "No pages to import." }; } @@ -65,7 +65,7 @@ export async function importPages( const { message } = (await res.json()) as { message: string }; return { ok: true, value: message }; -} +}; /** `exportPages`の認証情報 */ export interface ExportInit @@ -76,7 +76,7 @@ export interface ExportInit * * @param project exportしたいproject */ -export async function exportPages( +export const exportPages = async ( project: string, init: ExportInit, ): Promise< @@ -84,7 +84,7 @@ export async function exportPages( ExportedData, NotFoundError | NotPrivilegeError | NotLoggedInError > -> { +> => { const { sid, hostName, fetch, metadata } = setDefaults(init ?? {}); const path = `https://${hostName}/api/page-data/export/${project}.json?metadata=${metadata}`; @@ -111,4 +111,4 @@ export async function exportPages( const value = (await res.json()) as ExportedData; return { ok: true, value }; -} +}; diff --git a/rest/pages.ts b/rest/pages.ts index 4010e4e..361a17f 100644 --- a/rest/pages.ts +++ b/rest/pages.ts @@ -21,7 +21,7 @@ export interface GetPageOption extends BaseOptions { * @param title 取得したいページのtitle 大文字小文字は問わない * @param options オプション */ -export async function getPage( +export const getPage = async ( project: string, title: string, options?: GetPageOption, @@ -30,7 +30,7 @@ export async function getPage( Page, NotFoundError | NotLoggedInError | NotMemberError > -> { +> => { const { sid, hostName, fetch, followRename } = setDefaults(options ?? {}); const path = `https://${hostName}/api/pages/${project}/${ encodeTitleURI(title) @@ -59,7 +59,7 @@ export async function getPage( } const value = (await res.json()) as Page; return { ok: true, value }; -} +}; /** Options for `listPages()` */ export interface ListPagesOption extends BaseOptions { @@ -92,7 +92,7 @@ export interface ListPagesOption extends BaseOptions { * @param project 一覧したいproject * @param options オプション 取得範囲や並び順を決める */ -export async function listPages( +export const listPages = async ( project: string, options?: ListPagesOption, ): Promise< @@ -100,7 +100,7 @@ export async function listPages( PageList, NotFoundError | NotLoggedInError | NotMemberError > -> { +> => { const { sid, hostName, fetch, sort, limit, skip } = setDefaults( options ?? {}, ); @@ -134,4 +134,4 @@ export async function listPages( } const value = (await res.json()) as PageList; return { ok: true, value }; -} +}; diff --git a/rest/profile.ts b/rest/profile.ts index 8634951..8bddc5d 100644 --- a/rest/profile.ts +++ b/rest/profile.ts @@ -7,9 +7,9 @@ import { UnexpectedResponseError } from "./error.ts"; * * @param init connect.sid etc. */ -export async function getProfile( +export const getProfile = async ( init?: BaseOptions, -): Promise { +): Promise => { const { sid, hostName, fetch } = setDefaults(init ?? {}); const path = `https://${hostName}/api/users/me`; const res = await fetch( @@ -24,4 +24,4 @@ export async function getProfile( }); } return (await res.json()) as MemberUser | GuestUser; -} +}; diff --git a/rest/project.ts b/rest/project.ts index ee3bee6..18e832e 100644 --- a/rest/project.ts +++ b/rest/project.ts @@ -15,7 +15,7 @@ import { BaseOptions, Result, setDefaults } from "./util.ts"; * @param project project name to get * @param init connect.sid etc. */ -export async function getProject( +export const getProject = async ( project: string, init?: BaseOptions, ): Promise< @@ -23,7 +23,7 @@ export async function getProject( MemberProject | NotMemberProject, NotFoundError | NotMemberError | NotLoggedInError > -> { +> => { const { sid, hostName, fetch } = setDefaults(init ?? {}); const path = `https://${hostName}/api/projects/${project}`; const res = await fetch( @@ -49,4 +49,4 @@ export async function getProject( const value = (await res.json()) as MemberProject | NotMemberProject; return { ok: true, value }; -} +}; diff --git a/rest/replaceLinks.ts b/rest/replaceLinks.ts index f16e7ea..83634eb 100644 --- a/rest/replaceLinks.ts +++ b/rest/replaceLinks.ts @@ -19,7 +19,7 @@ import { ExtendedOptions, Result, setDefaults } from "./util.ts"; * @param init connect.sidなど * @return 置換されたリンクがあったページの数 */ -export async function replaceLinks( +export const replaceLinks = async ( project: string, from: string, to: string, @@ -29,7 +29,7 @@ export async function replaceLinks( number, NotFoundError | NotLoggedInError | NotMemberError > -> { +> => { const { sid, hostName, fetch, csrf } = setDefaults(init ?? {}); const path = `https://${hostName}/api/pages/${project}/replace/links`; @@ -65,4 +65,4 @@ export async function replaceLinks( // messageには"2 pages have been successfully updated!"というような文字列が入っているはず const { message } = (await res.json()) as { message: string }; return { ok: true, value: parseInt(message.match(/\d+/)?.[0] ?? "0") }; -} +}; From b2ec8a536fdbe24de3f5b9a1ca8434aca4b093b5 Mon Sep 17 00:00:00 2001 From: takker99 <37929109+takker99@users.noreply.github.com> Date: Fri, 25 Mar 2022 02:28:35 +0900 Subject: [PATCH 4/4] :rotating_light: fix lint --- rest/error.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/rest/error.ts b/rest/error.ts index 450250f..3b90966 100644 --- a/rest/error.ts +++ b/rest/error.ts @@ -19,6 +19,7 @@ export class UnexpectedResponseError extends Error { // @ts-ignore only available on V8 if (Error.captureStackTrace) { + // @ts-ignore only available on V8 Error.captureStackTrace(this, UnexpectedResponseError); } }