Skip to content

Commit 922948d

Browse files
committed
feat: curseforge support
1 parent 5c5a0fa commit 922948d

File tree

10 files changed

+720
-0
lines changed

10 files changed

+720
-0
lines changed

packages/curseforge/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Curseforge API
2+
3+
[![npm version](https://img.shields.io/npm/v/@xmcl/curseforge.svg)](https://www.npmjs.com/package/@xmcl/curseforge)
4+
[![Downloads](https://img.shields.io/npm/dm/@xmcl/curseforge.svg)](https://npmjs.com/@xmcl/curseforge)
5+
[![Install size](https://packagephobia.now.sh/badge?p=@xmcl/curseforge)](https://packagephobia.now.sh/result?p=@xmcl/curseforge)
6+
[![npm](https://img.shields.io/npm/l/@xmcl/minecraft-launcher-core.svg)](https://github.com/voxelum/minecraft-launcher-core-node/blob/master/LICENSE)
7+
[![Build Status](https://github.com/voxelum/minecraft-launcher-core-node/workflows/Build/badge.svg)](https://github.com/Voxelum/minecraft-launcher-core-node/actions?query=workflow%3ABuild)
8+
9+
Provide the curseforge (twitch) API in https://twitchappapi.docs.apiary.io/
10+
11+
## Usage
12+
13+
### Find Curseforge Mods by search keyword
14+
15+
You can use keyword to search
16+
17+
```ts
18+
import { searchAddons, SearchOptions } from '@xmcl/curseforge'
19+
const searchOptions: SearchOptions = {
20+
categoryId: 6, // 6 is mod,
21+
};
22+
const setting: GameSetting = searchAddons(settingString);
23+
const string: string = GameSetting.stringify(setting);
24+
```

packages/curseforge/http/base.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Agent } from "https";
2+
3+
/**
4+
* Abstract layer for http requester.
5+
*/
6+
export type HttpRequester =
7+
(option: {
8+
url: string;
9+
method: string;
10+
headers: { [key: string]: string };
11+
/**
12+
* Search string
13+
*/
14+
search?: { [key: string]: string | string[] | undefined };
15+
body?: object;
16+
userAgent?: Agent;
17+
}) => Promise<{
18+
body: string;
19+
statusMessage: string;
20+
statusCode: number;
21+
}>;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { HttpRequester } from "./base";
2+
3+
export const httpRequester: HttpRequester = async (option) => {
4+
const url = new URL(option.url);
5+
let body: any = undefined;
6+
let headers: { [key: string]: string } = option.headers;
7+
if (option.body) {
8+
headers["Content-Type"] = "application/json";
9+
body = JSON.stringify(option.body);
10+
}
11+
const response = await fetch(url.toString(), {
12+
body,
13+
headers,
14+
method: option.method,
15+
});
16+
return {
17+
body: await response.text(),
18+
statusCode: response.status,
19+
statusMessage: response.statusText,
20+
};
21+
}

packages/curseforge/http/index.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { request as http, RequestOptions, ClientRequest, IncomingMessage } from "http";
2+
import { request as https } from "https";
3+
import { URL } from "url";
4+
import { HttpRequester } from "./base";
5+
6+
export const httpRequester: HttpRequester = async (option) => {
7+
let headers: Record<string, string> = { ...option.headers };
8+
let requestOption: RequestOptions = {
9+
method: option.method,
10+
headers,
11+
agent: option.userAgent,
12+
}
13+
let url = new URL(option.url);
14+
15+
let body: Buffer | undefined;
16+
if (option.body) {
17+
headers["Content-Type"] = "application/json";
18+
body = Buffer.from(JSON.stringify(option.body), "utf-8");
19+
}
20+
return new Promise<{
21+
body: string;
22+
statusMessage: string;
23+
statusCode: number;
24+
}>((resolve, reject) => {
25+
function handleMessage(message: IncomingMessage) {
26+
let buffers = [] as Buffer[];
27+
message.on("data", (buf) => { buffers.push(buf); });
28+
message.on("end", () => {
29+
resolve({
30+
body: Buffer.concat(buffers).toString("utf-8"),
31+
statusCode: message.statusCode || -1,
32+
statusMessage: message.statusMessage || "",
33+
});
34+
});
35+
message.on("error", reject);
36+
}
37+
let req: ClientRequest;
38+
if (url.protocol === "https:") {
39+
req = https(url, requestOption, handleMessage);
40+
} else if (url.protocol === "http:") {
41+
req = http(url, requestOption, handleMessage);
42+
} else {
43+
reject(new Error(`Unsupported protocol ${url.protocol}`));
44+
return;
45+
}
46+
req.on("error", reject);
47+
if (body) {
48+
req.write(body);
49+
}
50+
req.end();
51+
});
52+
}

packages/curseforge/http/package.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "util",
3+
"private": true,
4+
"version": "0.0.0",
5+
"main": "./index.cjs.js",
6+
"module": "./index.js",
7+
"browser": {
8+
"./index.js": "./index.browser.js",
9+
"./index.cjs.js": "./index.browser.cjs.js"
10+
},
11+
"sideEffects": false,
12+
"dependencies": {
13+
}
14+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"files": [
3+
"index.ts",
4+
"index.browser.ts",
5+
"base.ts"
6+
],
7+
"compilerOptions": {
8+
"target": "es2018",
9+
"module": "CommonJS",
10+
"outDir": "cjs",
11+
"lib": [
12+
"dom"
13+
],
14+
"experimentalDecorators": true,
15+
"declaration": true,
16+
"downlevelIteration": true,
17+
"strict": true,
18+
"inlineSourceMap": true,
19+
"inlineSources": true,
20+
"moduleResolution": "node",
21+
"esModuleInterop": true,
22+
"allowSyntheticDefaultImports": true
23+
}
24+
}

0 commit comments

Comments
 (0)