init
This commit is contained in:
commit
a5913efeaa
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"deno.enable": true,
|
||||
"deno.lint": true,
|
||||
"deno.unstable": true
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
# Anilist Wrapper
|
||||
|
||||
An UNOFFICIAL wrapper for the anilist api written in typescript.
|
||||
|
||||
You can visit the official graphql docs for anilist [here](https://anilist.github.io/ApiV2-GraphQL-Docs/) to find out
|
||||
everything you can do[^*].
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
## Status
|
||||
|
||||
To see the current status of the wrapper check the [todo](TODO.md) list.
|
||||
|
||||
> **Warning** As of v0.12 the only way to create queries is `query.<queryName>`
|
||||
|
||||
## Usage
|
||||
|
||||
### Creating a query
|
||||
|
||||
```js
|
||||
const query = Client().query.MediaList()
|
||||
```
|
||||
|
||||
### Query arguments
|
||||
|
||||
The queries can accept either an object of `MediaListArguments`.
|
||||
|
||||
```js
|
||||
const queryByUserName = Client().query.MediaList({ userName: "Josh" })
|
||||
/*
|
||||
query ($id: Int) {
|
||||
MediaList(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
const queryById = Client().query.MediaList({ id: 1 })
|
||||
/*
|
||||
query ($userName: String) {
|
||||
MediaList(userName: $userName) {
|
||||
id
|
||||
}
|
||||
}
|
||||
*/
|
||||
```
|
||||
|
||||
### Creating the query
|
||||
|
||||
#### Other Queries
|
||||
|
||||
##### Fetching without building the query
|
||||
|
||||
If you build the query and try to fetch it without telling which fields to return it will default to returning `id` to
|
||||
avoid errors.
|
||||
|
||||
```js
|
||||
const query = Client().query.MediaList({ userName: "Josh" })
|
||||
|
||||
await query.fetch()
|
||||
```
|
||||
=>
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"MediaList": {
|
||||
"id": 5318
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### Creating a complete search query
|
||||
|
||||
As the library follows the builder pattern you can just nest functions until you have every field you want.
|
||||
|
||||
```js
|
||||
const query = Client().query.MediaList({ userName: "Josh" }, (fields) => {
|
||||
fields.withId()
|
||||
fields.withUserId()
|
||||
.withPrivate()
|
||||
.withStartedAt((fields) => fields.withYear().withMonth().withDay())
|
||||
.withCompletedAt((fields) => fields.withYear().withMonth().withDay())
|
||||
})
|
||||
|
||||
await query.fetch()
|
||||
```
|
||||
=>
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"MediaList": {
|
||||
"id": 5318,
|
||||
"userId": 1,
|
||||
"private": false,
|
||||
"startedAt": { "year": null, "month": null, "day": null },
|
||||
"completedAt": { "year": 2017, "month": 11, "day": 24 }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## OAuth
|
||||
|
||||
The library provides 2 helpers methods for OAuth and i will explain them here
|
||||
|
||||
### Using Token
|
||||
Once you have a token, you can pass it into `Client({ token })`.
|
||||
|
||||
### Getting a token from an authorization code grant
|
||||
|
||||
This helper method allows you to convert the Authorization Code Grant into an access token. The response is a type `Authorization`.
|
||||
|
||||
```ts
|
||||
const token = Client().auth.getToken({
|
||||
client_id: "<the id as a string>",
|
||||
client_secret: "<the client secret>",
|
||||
code: "<code>",
|
||||
redirect_uri: "<the redirect uri, this is required>"
|
||||
})
|
||||
```
|
||||
=>
|
||||
```json
|
||||
{
|
||||
"token_type": "string",
|
||||
/* A full year in seconds */
|
||||
"expires_in": "number",
|
||||
"access_token": "string",
|
||||
/* Currently not used for anything */
|
||||
"refresh_token": "string"
|
||||
}
|
||||
```
|
||||
[^*]: Not everything is supported yet, please refer to the todo list to see what has full implementation or open an
|
||||
issue to talk about it
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"fmt": {
|
||||
"lineWidth": 120,
|
||||
"semiColons": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,671 @@
|
|||
import { Fetch, MediaListArguments, ThreadCommentSort, UpdateQuery, Variables } from "./types/Anilist.ts"
|
||||
import type { AtLeastOne } from "./types/AtLeastOne.ts"
|
||||
|
||||
const typeMap = {
|
||||
User: { sort: "[UserSort]" },
|
||||
about: { asHtml: "Boolean" },
|
||||
releaseYear: { sort: "[UserStatisticsSort]" },
|
||||
misc: {
|
||||
page: "Int",
|
||||
perPage: "Int",
|
||||
id: "Int",
|
||||
userId: "Int",
|
||||
comment: "String",
|
||||
name: "String",
|
||||
userName: "String",
|
||||
isModerator: "Boolean",
|
||||
search: "String",
|
||||
limit: "Int",
|
||||
format: "ScoreFormat",
|
||||
asArray: "Boolean",
|
||||
mediaId: "Int",
|
||||
},
|
||||
}
|
||||
|
||||
const tab = (n: number) => new Array(n + 1).fill("").join("\t")
|
||||
const t = tab
|
||||
|
||||
const updateOperation = (
|
||||
{ query, variables, field, level, hasSubField }: UpdateQuery,
|
||||
) => {
|
||||
if (!query) query = `${field[level]} (%root) {\n\t%${field[level]}\n}`
|
||||
const convertedType: [string[], string[]] = [[], []]
|
||||
if (variables) {
|
||||
const variable = Object.entries(variables)
|
||||
for (let i = 0; i <= variable.length - 1; i++) {
|
||||
convertedType[0].push(`${variable[i][0]}: $${variable[i][0]}`)
|
||||
convertedType[1].push(
|
||||
`$${variable[i][0]}: ${
|
||||
variable[i][0] in typeMap
|
||||
? typeMap[variable[i][0] as keyof typeof typeMap]
|
||||
: typeMap["misc"][variable[i][0] as keyof typeof typeMap["misc"]]
|
||||
}`,
|
||||
)
|
||||
}
|
||||
query = query.replace(
|
||||
`%root`,
|
||||
`${convertedType[1].join(", ")}, %root`,
|
||||
)
|
||||
}
|
||||
query = query
|
||||
.replace(
|
||||
`%${field[level - 1]}`,
|
||||
`${field[level]}${variables ? ` (${convertedType[0].join(", ")})` : ""}${
|
||||
hasSubField ? `${` {\n${t(level + 1)}%${field.at(-1)}\n${t(level)}}`}` : ""
|
||||
}\n${t(level)}%${field[level - 1]}`,
|
||||
)
|
||||
// console.log({ query, variables, field, level })
|
||||
|
||||
return {
|
||||
get() {
|
||||
// return query
|
||||
return query!.replace(/(\n|,)?(\t+| )%\w+\b/g, "")
|
||||
},
|
||||
set(
|
||||
{ subField, variables, hasSubField, level }:
|
||||
& Pick<
|
||||
UpdateQuery,
|
||||
"hasSubField" | "variables"
|
||||
>
|
||||
& { subField: string; level: number },
|
||||
) {
|
||||
// const f = [...field]
|
||||
field.splice(level, 0, subField)
|
||||
while (field.length - 1 > level) field.pop()
|
||||
|
||||
return updateOperation({
|
||||
query,
|
||||
variables,
|
||||
// level: depth ? depth : level + 1,
|
||||
level,
|
||||
field,
|
||||
hasSubField,
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const fetch: Fetch = async (init) => {
|
||||
let host:
|
||||
| "https://graphql.anilist.co"
|
||||
| "https://anilist.co/api/v2/oauth/token"
|
||||
const body = {}
|
||||
const headers = new Headers({
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
})
|
||||
if ("code" in init) {
|
||||
const {
|
||||
client_id,
|
||||
client_secret,
|
||||
code,
|
||||
redirect_uri,
|
||||
grant_type = "authorization_code",
|
||||
} = init
|
||||
host = "https://anilist.co/api/v2/oauth/token"
|
||||
Object.assign(body, {
|
||||
grant_type,
|
||||
client_id,
|
||||
client_secret,
|
||||
redirect_uri,
|
||||
code,
|
||||
})
|
||||
} else {
|
||||
host = "https://graphql.anilist.co"
|
||||
const { query, variables, token } = init
|
||||
Object.assign(body, { query, variables })
|
||||
if (token) headers.set("Authorization", `Bearer ${token}`)
|
||||
}
|
||||
const res = await self.fetch(host, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
if (res.status !== 200) {
|
||||
throw new Error("Something went wrong.", {
|
||||
cause: JSON.stringify(await res.json()),
|
||||
})
|
||||
}
|
||||
return await res.json()
|
||||
}
|
||||
|
||||
const fuzzyDateFields = (
|
||||
query: ReturnType<typeof updateOperation>[],
|
||||
level: number,
|
||||
) => ({
|
||||
withDay() {
|
||||
query[0] = query[0].set({ subField: "day", level })
|
||||
return this
|
||||
},
|
||||
withMonth() {
|
||||
query[0] = query[0].set({ subField: "month", level })
|
||||
return this
|
||||
},
|
||||
withYear() {
|
||||
query[0] = query[0].set({ subField: "year", level })
|
||||
return this
|
||||
},
|
||||
})
|
||||
const MediaListFields = (
|
||||
query: ReturnType<typeof updateOperation>[],
|
||||
_level: number,
|
||||
) => ({
|
||||
withCompletedAt(fn: (fields: ReturnType<typeof fuzzyDateFields>) => void) {
|
||||
query[0] = query[0].set({
|
||||
subField: "completedAt",
|
||||
level: 2,
|
||||
hasSubField: true,
|
||||
})
|
||||
let tmpQuery
|
||||
fn(fuzzyDateFields(tmpQuery = [query[0]], 3))
|
||||
query[0] = tmpQuery[0]
|
||||
return this
|
||||
},
|
||||
withStartedAt(fn: (fields: ReturnType<typeof fuzzyDateFields>) => void) {
|
||||
query[0] = query[0].set({
|
||||
subField: "startedAt",
|
||||
level: 2,
|
||||
hasSubField: true,
|
||||
})
|
||||
let tmpQuery
|
||||
fn(fuzzyDateFields(tmpQuery = [query[0]], 3))
|
||||
query[0] = tmpQuery[0]
|
||||
return this
|
||||
},
|
||||
withId() {
|
||||
query[0] = query[0].set({ subField: "id", level: 2 })
|
||||
return this
|
||||
},
|
||||
withUserId() {
|
||||
query[0] = query[0].set({ subField: "userId", level: 2 })
|
||||
return this
|
||||
},
|
||||
withMediaId() {
|
||||
query[0] = query[0].set({ subField: "mediaId", level: 2 })
|
||||
return this
|
||||
},
|
||||
withStatus() {
|
||||
query[0] = query[0].set({ subField: "status", level: 2 })
|
||||
return this
|
||||
},
|
||||
withProgress() {
|
||||
query[0] = query[0].set({ subField: "progress", level: 2 })
|
||||
return this
|
||||
},
|
||||
withProgressVolumes() {
|
||||
query[0] = query[0].set({ subField: "progressVolumes", level: 2 })
|
||||
return this
|
||||
},
|
||||
withRepeat() {
|
||||
query[0] = query[0].set({ subField: "repeat", level: 2 })
|
||||
return this
|
||||
},
|
||||
withPriority() {
|
||||
query[0] = query[0].set({ subField: "priority", level: 2 })
|
||||
return this
|
||||
},
|
||||
withPrivate() {
|
||||
query[0] = query[0].set({ subField: "private", level: 2 })
|
||||
return this
|
||||
},
|
||||
withNotes() {
|
||||
query[0] = query[0].set({ subField: "notes", level: 2 })
|
||||
return this
|
||||
},
|
||||
withAdvancedScores() {
|
||||
query[0] = query[0].set({ subField: "advancedScores", level: 2 })
|
||||
return this
|
||||
},
|
||||
withHiddenFromStatusLists() {
|
||||
query[0] = query[0].set({
|
||||
subField: "hiddenFromStatusLists",
|
||||
level: 2,
|
||||
})
|
||||
return this
|
||||
},
|
||||
withUpdatedAt() {
|
||||
query[0] = query[0].set({ subField: "updatedAt", level: 2 })
|
||||
return this
|
||||
},
|
||||
withCreatedAt() {
|
||||
query[0] = query[0].set({ subField: "createdAt", level: 2 })
|
||||
return this
|
||||
},
|
||||
})
|
||||
const threadCommentFields = (
|
||||
query: ReturnType<typeof updateOperation>[],
|
||||
level: number,
|
||||
) => ({
|
||||
/** The id of the comment */
|
||||
withId() {
|
||||
query[0] = query[0].set({ subField: "id", level })
|
||||
return this
|
||||
},
|
||||
/** The user id of the comment's owner */
|
||||
withUserId() {
|
||||
query[0] = query[0].set({ subField: "userId", level })
|
||||
return this
|
||||
},
|
||||
/** The id of thread the comment belongs to */
|
||||
withThreadId() {
|
||||
query[0] = query[0].set({ subField: "threadId", level })
|
||||
return this
|
||||
},
|
||||
/** The text content of the comment (Markdown) */
|
||||
withComment(args?: { asHtml: boolean }) {
|
||||
query[0] = query[0].set({ subField: "comment", level, variables: args })
|
||||
return this
|
||||
},
|
||||
/** The amount of likes the comment has */
|
||||
withLikeCount() {
|
||||
query[0] = query[0].set({ subField: "likeCount", level })
|
||||
return this
|
||||
},
|
||||
/** If the currently authenticated user liked the comment */
|
||||
withIsLiked() {
|
||||
query[0] = query[0].set({ subField: "isLiked", level })
|
||||
return this
|
||||
},
|
||||
/** The url for the comment page on the AniList website */
|
||||
withSiteUrl() {
|
||||
query[0] = query[0].set({ subField: "siteUrl", level })
|
||||
return this
|
||||
},
|
||||
/** The time of the comments creation */
|
||||
withCreatedAt() {
|
||||
query[0] = query[0].set({ subField: "createdAt", level })
|
||||
return this
|
||||
},
|
||||
/** The time of the comments last update */
|
||||
withUpdatedAt() {
|
||||
query[0] = query[0].set({ subField: "updatedAt", level })
|
||||
return this
|
||||
},
|
||||
/** The thread the comment belongs to */
|
||||
withThread() {
|
||||
query[0] = query[0].set({ subField: "thread", level })
|
||||
return this
|
||||
},
|
||||
/** The user who created the comment */
|
||||
withUser() {
|
||||
query[0] = query[0].set({ subField: "user", level })
|
||||
return this
|
||||
},
|
||||
/** The users who liked the comment */
|
||||
withLikes() {
|
||||
query[0] = query[0].set({ subField: "likes", level })
|
||||
return this
|
||||
},
|
||||
/** The comment's child reply comments */
|
||||
withChildComments() {
|
||||
query[0] = query[0].set({ subField: "childComments", level })
|
||||
return this
|
||||
},
|
||||
/** If the comment tree is locked and may not receive replies or edits */
|
||||
withIsLocked() {
|
||||
query[0] = query[0].set({ subField: "isLocked", level })
|
||||
return this
|
||||
},
|
||||
})
|
||||
|
||||
export const Client = function (auth?: { token: string }) {
|
||||
const variables: Variables = {}
|
||||
let operation: ReturnType<typeof updateOperation>
|
||||
return {
|
||||
get auth() {
|
||||
return {
|
||||
getToken(init: {
|
||||
code: string
|
||||
client_id: string
|
||||
client_secret: string
|
||||
redirect_uri: string
|
||||
}) {
|
||||
return fetch(init)
|
||||
},
|
||||
}
|
||||
},
|
||||
get query() {
|
||||
operation = updateOperation({
|
||||
field: ["query"],
|
||||
level: 0,
|
||||
})
|
||||
|
||||
return {
|
||||
get raw() {
|
||||
return {
|
||||
get variable() {
|
||||
return variables
|
||||
},
|
||||
get() {
|
||||
return operation.get()
|
||||
},
|
||||
}
|
||||
},
|
||||
fetch() {
|
||||
return fetch({ query: this.raw.get()!, variables })
|
||||
},
|
||||
Page() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Media query */
|
||||
Media() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Media Trend query */
|
||||
MediaTrend() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Airing schedule query */
|
||||
AiringSchedule() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Character query */
|
||||
Character() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Staff query */
|
||||
Staff() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Media list query */
|
||||
MediaList(
|
||||
args: AtLeastOne<MediaListArguments> & Variables,
|
||||
fn?: (fields: ReturnType<typeof MediaListFields>) => void,
|
||||
) {
|
||||
Object.assign(variables, args)
|
||||
operation = operation.set({
|
||||
subField: "MediaList",
|
||||
variables: args,
|
||||
hasSubField: true,
|
||||
level: 1,
|
||||
})
|
||||
if (!fn) operation = operation.set({ subField: "id", level: 2 })
|
||||
else {
|
||||
let tmpQuery
|
||||
fn(MediaListFields(tmpQuery = [operation], 2))
|
||||
operation = tmpQuery[0]
|
||||
}
|
||||
return this
|
||||
},
|
||||
/** Media list collection query, provides list pre-grouped by status & custom lists. User ID and Media Type arguments required. */
|
||||
MediaListCollection() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Collection of all the possible media genres */
|
||||
GenreCollection() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Collection of all the possible media tags */
|
||||
MediaTagCollection() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** User query */
|
||||
User() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Get the currently authenticated user */
|
||||
Viewer() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Notification query */
|
||||
Notification() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Studio query */
|
||||
Studio() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Review query */
|
||||
Review() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Activity query */
|
||||
Activity() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Activity reply query */
|
||||
ActivityReply() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Follow query */
|
||||
Following() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Follow query */
|
||||
Follower() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Thread query */
|
||||
Thread() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Comment query */
|
||||
ThreadComment(
|
||||
args: AtLeastOne<
|
||||
{
|
||||
/** Filter by the comment id */
|
||||
id: number
|
||||
/** Filter by the thread id */
|
||||
threadId: number
|
||||
/** Filter by the user id of the comment's creator */
|
||||
userId: number
|
||||
/** The order the results will be returned in */
|
||||
sort: ThreadCommentSort[]
|
||||
} & Variables
|
||||
>,
|
||||
fn?: (fields: ReturnType<typeof threadCommentFields>) => void,
|
||||
) {
|
||||
Object.assign(variables, args)
|
||||
operation = operation.set({
|
||||
level: 1,
|
||||
subField: "ThreadComment",
|
||||
hasSubField: true,
|
||||
variables: args,
|
||||
})
|
||||
if (!fn) operation = operation.set({ level: 2, subField: "id" })
|
||||
else {
|
||||
let tmpQuery
|
||||
fn(threadCommentFields(tmpQuery = [operation], 2))
|
||||
operation = tmpQuery[0]
|
||||
}
|
||||
return this
|
||||
},
|
||||
/** Recommendation query */
|
||||
Recommendation() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Like query */
|
||||
Like() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Provide AniList markdown to be converted to html (Requires auth) */
|
||||
Markdown() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
AniChartUser() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Site statistics query */
|
||||
SiteStatistics() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** ExternalLinkSource collection query */
|
||||
ExternalLinkSourceCollection() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
}
|
||||
},
|
||||
get mutation() {
|
||||
operation = updateOperation({
|
||||
field: ["mutation"],
|
||||
level: 0,
|
||||
})
|
||||
|
||||
return {
|
||||
get raw() {
|
||||
return {
|
||||
get variable() {
|
||||
return variables
|
||||
},
|
||||
get() {
|
||||
return operation.get()
|
||||
},
|
||||
}
|
||||
},
|
||||
fetch() {
|
||||
return fetch({
|
||||
query: this.raw.get()!,
|
||||
variables,
|
||||
token: auth?.token,
|
||||
})
|
||||
},
|
||||
/** Update current user options */
|
||||
UpdateUser() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Create or update a media list entry */
|
||||
SaveMediaListEntry() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Update multiple media list entries to the same values */
|
||||
UpdateMediaListEntries() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Delete a media list entry */
|
||||
DeleteMediaListEntry() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Delete a custom list and remove the list entries from it */
|
||||
DeleteCustomList() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Create or update text activity for the currently authenticated user */
|
||||
SaveTextActivity() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Create or update message activity for the currently authenticated user */
|
||||
SaveMessageActivity() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Update list activity (Mod Only) */
|
||||
SaveListActivity() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Delete an activity item of the authenticated users */
|
||||
DeleteActivity() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Toggle activity to be pinned to the top of the user's activity feed */
|
||||
ToggleActivityPin() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Toggle the subscription of an activity item */
|
||||
ToggleActivitySubscription() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Create or update an activity reply */
|
||||
SaveActivityReply() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Delete an activity reply of the authenticated users */
|
||||
DeleteActivityReply() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Add or remove a like from a likeable type. Returns all the users who liked the same model */
|
||||
ToggleLike() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Add or remove a like from a likeable type. */
|
||||
ToggleLikeV2() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Toggle the un/following of a user */
|
||||
ToggleFollow() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Favourite or unfavourite an anime, manga, character, staff member, or studio */
|
||||
ToggleFavourite() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Update the order favourites are displayed in */
|
||||
UpdateFavouriteOrder() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Create or update a review */
|
||||
SaveReview() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Delete a review */
|
||||
DeleteReview() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Rate a review */
|
||||
RateReview() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Recommendation a media */
|
||||
SaveRecommendation() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Create or update a forum thread */
|
||||
SaveThread() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Delete a thread */
|
||||
DeleteThread() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Toggle the subscription of a forum thread */
|
||||
ToggleThreadSubscription() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
/** Create or update a thread comment */
|
||||
SaveThreadComment(
|
||||
args:
|
||||
& ({
|
||||
/** The comment id, required for updating */
|
||||
id: number
|
||||
} | {
|
||||
/** The id of thread the comment belongs to */
|
||||
threadId: number
|
||||
} | {
|
||||
/** The id of thread comment to reply to */
|
||||
parentCommentId: number
|
||||
})
|
||||
& {
|
||||
/** The comment markdown text */
|
||||
comment: string
|
||||
/** If the comment tree should be locked. (Mod Only) */
|
||||
locked?: boolean
|
||||
},
|
||||
fn?: (fields: ReturnType<typeof threadCommentFields>) => void,
|
||||
) {
|
||||
Object.assign(variables, args)
|
||||
operation = operation.set({
|
||||
level: 1,
|
||||
subField: "SaveThreadComment",
|
||||
hasSubField: true,
|
||||
variables: args,
|
||||
})
|
||||
if (!fn) operation = operation.set({ level: 2, subField: "id" })
|
||||
else {
|
||||
let tmpQuery
|
||||
fn(threadCommentFields(tmpQuery = [operation], 2))
|
||||
operation = tmpQuery[0]
|
||||
}
|
||||
return this
|
||||
},
|
||||
/** Delete a thread comment */
|
||||
DeleteThreadComment() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
UpdateAniChartSettings() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
UpdateAniChartHighlights() {
|
||||
throw "To be Implemented"
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
type Int = number
|
||||
type Float = number
|
||||
type String = string
|
||||
type Boolean = boolean
|
||||
type Json = Record<string, unknown>
|
||||
|
||||
/** Media list sort enums */
|
||||
export enum MediaListSort {
|
||||
MEDIA_ID = "MEDIA_ID",
|
||||
MEDIA_ID_DESC = "MEDIA_ID_DESC",
|
||||
SCORE = "SCORE",
|
||||
SCORE_DESC = "SCORE_DESC",
|
||||
STATUS = "STATUS",
|
||||
STATUS_DESC = "STATUS_DESC",
|
||||
PROGRESS = "PROGRESS",
|
||||
PROGRESS_DESC = "PROGRESS_DESC",
|
||||
PROGRESS_VOLUMES = "PROGRESS_VOLUMES",
|
||||
PROGRESS_VOLUMES_DESC = "PROGRESS_VOLUMES_DESC",
|
||||
REPEAT = "REPEAT",
|
||||
REPEAT_DESC = "REPEAT_DESC",
|
||||
PRIORITY = "PRIORITY",
|
||||
PRIORITY_DESC = "PRIORITY_DESC",
|
||||
STARTED_ON = "STARTED_ON",
|
||||
STARTED_ON_DESC = "STARTED_ON_DESC",
|
||||
FINISHED_ON = "FINISHED_ON",
|
||||
FINISHED_ON_DESC = "FINISHED_ON_DESC",
|
||||
ADDED_TIME = "ADDED_TIME",
|
||||
ADDED_TIME_DESC = "ADDED_TIME_DESC",
|
||||
UPDATED_TIME = "UPDATED_TIME",
|
||||
UPDATED_TIME_DESC = "UPDATED_TIME_DESC",
|
||||
MEDIA_TITLE_ROMAJI = "MEDIA_TITLE_ROMAJI",
|
||||
MEDIA_TITLE_ROMAJI_DESC = "MEDIA_TITLE_ROMAJI_DESC",
|
||||
MEDIA_TITLE_ENGLISH = "MEDIA_TITLE_ENGLISH",
|
||||
MEDIA_TITLE_ENGLISH_DESC = "MEDIA_TITLE_ENGLISH_DESC",
|
||||
MEDIA_TITLE_NATIVE = "MEDIA_TITLE_NATIVE",
|
||||
MEDIA_TITLE_NATIVE_DESC = "MEDIA_TITLE_NATIVE_DESC",
|
||||
MEDIA_POPULARITY = "MEDIA_POPULARITY",
|
||||
MEDIA_POPULARITY_DESC = "MEDIA_POPULARITY_DESC",
|
||||
}
|
||||
|
||||
/** 8 digit long date integer (YYYYMMDD). Unknown dates represented by 0. */
|
||||
export type FuzzyDateInt = string
|
||||
|
||||
/** Thread comments sort enums */
|
||||
export enum ThreadCommentSort {
|
||||
ID = "ID",
|
||||
ID_DESC = "ID_DESC",
|
||||
}
|
||||
|
||||
/** Media list scoring type */
|
||||
export enum ScoreFormat {
|
||||
/** An integer from 0-100 */
|
||||
POINT_100 = "POINT_100",
|
||||
/** A float from 0-10 with 1 decimal place */
|
||||
POINT_10_DECIMAL = "POINT_10_DECIMAL",
|
||||
/** An integer from 0-10 */
|
||||
POINT_10 = "POINT_10",
|
||||
/** An integer from 0-5. Should be represented in Stars */
|
||||
POINT_5 = "POINT_5",
|
||||
/** An integer from 0-3. Should be represented in Smileys. 0 => No Score, 1 => :(, 2 => :|, 3 => :) */
|
||||
POINT_3 = "POINT_3",
|
||||
}
|
||||
|
||||
export type Variables = { [arg: string]: string | number | boolean | string[] }
|
||||
|
||||
export type UpdateQuery = {
|
||||
query?: string
|
||||
variables?: Variables
|
||||
field: string[]
|
||||
level: number
|
||||
hasSubField?: true
|
||||
}
|
||||
|
||||
export type Authorization = {
|
||||
token_type: string
|
||||
expires_in: number
|
||||
access_token: string
|
||||
/** Currently not used for anything */
|
||||
refresh_token: string
|
||||
}
|
||||
|
||||
export type Fetch = {
|
||||
(
|
||||
init:
|
||||
& Record<
|
||||
| "code"
|
||||
| "client_id"
|
||||
| "client_secret"
|
||||
| "redirect_uri",
|
||||
string
|
||||
>
|
||||
& { "grant_type"?: "authorization_code" },
|
||||
): Promise<Authorization>
|
||||
(
|
||||
init: { query: string; variables: Variables; token?: string },
|
||||
): Promise<Response>
|
||||
}
|
||||
|
||||
export type Fields = (Readonly<
|
||||
[string] | [string, true] | [string, true, Fields]
|
||||
>)[]
|
||||
|
||||
export type MediaListArguments = {
|
||||
/** Filter by a list entry's id */
|
||||
id: number
|
||||
/** Filter by a user's id */
|
||||
userId: number
|
||||
/** Filter by a user's name */
|
||||
userName: string
|
||||
/** Filter by the list entries media type */
|
||||
type: MediaType
|
||||
/** Filter by the watching/reading status */
|
||||
status: MediaListStatus
|
||||
/** Filter by the media id of the list entry */
|
||||
mediaId: number
|
||||
/** Filter list entries to users who are being followed by the authenticated user */
|
||||
isFollowing: boolean
|
||||
/** Filter by note words and #tags */
|
||||
notes: string
|
||||
/** Filter by the date the user started the media */
|
||||
startedAt: FuzzyDateInt
|
||||
/** Filter by the date the user completed the media */
|
||||
completedAt: FuzzyDateInt
|
||||
/** Limit to only entries also on the auth user's list. Requires user id or name arguments */
|
||||
compareWithAuthList: boolean
|
||||
/** Filter by a user's id */
|
||||
userId_in: number[]
|
||||
/** Filter by the watching/reading status */
|
||||
status_in: MediaListStatus[]
|
||||
/** Filter by the watching/reading status */
|
||||
status_not_in: MediaListStatus[]
|
||||
/** Filter by the watching/reading status */
|
||||
status_not: MediaListStatus
|
||||
/** Filter by the media id of the list entry */
|
||||
mediaId_in: number[]
|
||||
/** Filter by the media id of the list entry */
|
||||
mediaId_not_in: number[]
|
||||
/** Filter by note words and #tags */
|
||||
notes_like: string
|
||||
/** Filter by the date the user started the media */
|
||||
startedAt_greater: FuzzyDateInt
|
||||
/** Filter by the date the user started the media */
|
||||
startedAt_lesser: FuzzyDateInt
|
||||
/** Filter by the date the user started the media */
|
||||
startedAt_like: string
|
||||
/** Filter by the date the user completed the media */
|
||||
completedAt_greater: FuzzyDateInt
|
||||
/** Filter by the date the user completed the media */
|
||||
completedAt_lesser: FuzzyDateInt
|
||||
/** Filter by the date the user completed the media */
|
||||
completedAt_like: string
|
||||
/** The order the results will be returned in */
|
||||
sort: MediaListSort[]
|
||||
}
|
||||
|
||||
export type Response<T = Record<string, any>> = {
|
||||
data: T
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
export type AtLeastOne<O> = {
|
||||
[P in keyof O]:
|
||||
& { [L in P]: O[L] }
|
||||
& { [L in Exclude<keyof O, P>]?: O[L] }
|
||||
}[keyof O]
|
|
@ -0,0 +1,7 @@
|
|||
const Case = {
|
||||
up: "toUpperCase",
|
||||
down: "toLowerCase",
|
||||
} as const
|
||||
|
||||
export const toCase = <T extends string>(type: keyof typeof Case, str: T) =>
|
||||
str[0][Case[type]]() + str.slice(1) as Capitalize<T>
|
Loading…
Reference in New Issue