├── .editorconfig ├── .github └── workflows │ ├── npm-publish-github-packages.yml │ └── npm-publish.yml ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── docs ├── .nojekyll ├── assets │ ├── highlight.css │ ├── icons.css │ ├── icons.png │ ├── icons@2x.png │ ├── main.js │ ├── search.js │ ├── style.css │ ├── widgets.png │ └── widgets@2x.png ├── classes │ ├── Category.html │ ├── Curseforge.html │ ├── Exceptions.ErrorBadRequest.html │ ├── Exceptions.ErrorInternalServer.html │ ├── Exceptions.ErrorNotFound.html │ ├── Exceptions.ErrorServiceUnavailable.html │ ├── Game.html │ ├── Mod.html │ └── ModFile.html ├── enums │ ├── Enums.CoreApiStatus.html │ ├── Enums.CoreStatus.html │ ├── Enums.FileHashAlgorithms.html │ ├── Enums.FileRelationType.html │ ├── Enums.FileReleaseType.html │ ├── Enums.FileStatus.html │ ├── Enums.GameVersionStatus.html │ ├── Enums.GameVersionTypeStatus.html │ ├── Enums.ModLoaderInstallMethod.html │ ├── Enums.ModLoaderType.html │ ├── Enums.ModStatus.html │ └── Enums.ModsSearchSortField.html ├── index.html ├── modules.html └── modules │ ├── Enums.html │ ├── Exceptions.html │ └── Types.html ├── package-lock.json ├── package.json ├── src ├── index.ts ├── objects │ ├── Category.ts │ ├── Game.ts │ ├── MinecraftModLoader.ts │ ├── MinecraftVersion.ts │ ├── Mod.ts │ ├── ModFile.ts │ ├── enums.ts │ ├── exceptions.ts │ ├── index.ts │ ├── interfaces.ts │ └── types.ts └── utils.ts ├── tsconfig.json └── typedoc.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 4 9 | end_of_line = crlf 10 | charset = utf-8 11 | trim_trailing_whitespace = false 12 | insert_final_newline = false -------------------------------------------------------------------------------- /.github/workflows/npm-publish-github-packages.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package [Github Packages] 5 | 6 | on: 7 | workflow_dispatch: 8 | release: 9 | types: [created] 10 | 11 | jobs: 12 | build-and-publish: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | packages: write 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: actions/setup-node@v3 20 | with: 21 | node-version: 16 22 | - run: npm config set https://npm.pkg.github.com/:_authToken $AUTH_TOKEN 23 | - run: npm ci 24 | - run: npm run build-project 25 | - run: npm test 26 | - run: npm publish 27 | env: 28 | AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} 29 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package [NPM] 5 | 6 | on: 7 | workflow_dispatch: 8 | release: 9 | types: [created] 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: actions/setup-node@v3 17 | with: 18 | node-version: 16 19 | registry-url: https://registry.npmjs.org/ 20 | - run: npm ci 21 | - run: npm run build-project 22 | - run: npm test 23 | - run: npm publish 24 | env: 25 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | temp/ 4 | etc/ 5 | 6 | *.log 7 | 8 | # User Settings 9 | .vscode/ 10 | 11 | # jetbrains files 12 | .idea 13 | 14 | *.user.config 15 | 16 | yarn.lock -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | docs/ 3 | .vscode/ 4 | .github/ 5 | tsconfig.json 6 | typedoc.json 7 | *.log 8 | yarn.lock 9 | package-lock.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Mondanzo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-curseforge ![npm bundle size](https://img.shields.io/bundlephobia/min/node-curseforge) ![npm](https://img.shields.io/npm/dw/node-curseforge) 2 | 3 | A zero-dependencies NodeJS package to interact with the curseforge core api. 4 | Requires a CFCore authentication token (get one from [console.curseforge.com/#/api-keys](https://console.curseforge.com/#/api-keys) and yes you need to use a google account to login (sadly)). 5 | 6 | ### view on npm: [node-curseforge](https://www.npmjs.com/package/node-curseforge) 7 | 8 | ## Installation 9 | 10 | To install this package you can use either yarn or npm. 11 | 12 | `yarn add node-curseforge` 13 | 14 | or 15 | 16 | `npm install node-curseforge` 17 | 18 | --- 19 | 20 | ## How-To Use 21 | 22 | You can use this package mostly in two ways. Either you fetch a game first and interact over the game mostly with the api or you use the root class and give the required ids or objects directly. 23 | 24 | ### Using the Curseforge class 25 | 26 | ```javascript 27 | 28 | const {Curseforge} = require("node-curseforge"); 29 | 30 | let cf = new Curseforge(cf_token); 31 | 32 | let mc = cf.get_game("minecraft"); 33 | 34 | cf.search_mods(mc).then((mods) => { 35 | for(let mod of mods){ 36 | cf.get_description(mod).then((desc) => { 37 | console.log(desc); 38 | }); 39 | } 40 | }); 41 | 42 | ``` 43 | 44 | --- 45 | 46 | ### Using the Curseforge objects 47 | 48 | Using the methods on the objects themselves actually calls them using the curseforge instance and directly fills in the ids. 49 | 50 | ```javascript 51 | 52 | const {Curseforge} = require("node-curseforge") 53 | 54 | let cf = new Curseforge(cf_token); 55 | 56 | let mc = cf.get_game("minecraft"); 57 | 58 | mc.search_mods().then((mods) => { 59 | for(let mod of mods){ 60 | mod.get_description().then((desc) => { 61 | console.log(desc); 62 | }) 63 | } 64 | }) 65 | 66 | ``` 67 | 68 | ## Typescript Support 69 | 70 | The entire package is written in typescript and compiled to Javascript with an additional declaration file! You can therefore use this file with regular Javascript or with regular Typescript. 71 | 72 | ## Documentation 73 | 74 | You can find the (wip) documentation at [mondanzo.github.io/node-curseforge](https://mondanzo.github.io/node-curseforge). Most of the code is very self-explanatory though and overall also 1:1 identical with the [CFCore Docs](). 75 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. -------------------------------------------------------------------------------- /docs/assets/highlight.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --light-hl-0: #0000FF; 3 | --dark-hl-0: #569CD6; 4 | --light-hl-1: #000000; 5 | --dark-hl-1: #D4D4D4; 6 | --light-hl-2: #0070C1; 7 | --dark-hl-2: #4FC1FF; 8 | --light-hl-3: #795E26; 9 | --dark-hl-3: #DCDCAA; 10 | --light-hl-4: #A31515; 11 | --dark-hl-4: #CE9178; 12 | --light-hl-5: #001080; 13 | --dark-hl-5: #9CDCFE; 14 | --light-hl-6: #AF00DB; 15 | --dark-hl-6: #C586C0; 16 | --light-code-background: #F5F5F5; 17 | --dark-code-background: #1E1E1E; 18 | } 19 | 20 | @media (prefers-color-scheme: light) { :root { 21 | --hl-0: var(--light-hl-0); 22 | --hl-1: var(--light-hl-1); 23 | --hl-2: var(--light-hl-2); 24 | --hl-3: var(--light-hl-3); 25 | --hl-4: var(--light-hl-4); 26 | --hl-5: var(--light-hl-5); 27 | --hl-6: var(--light-hl-6); 28 | --code-background: var(--light-code-background); 29 | } } 30 | 31 | @media (prefers-color-scheme: dark) { :root { 32 | --hl-0: var(--dark-hl-0); 33 | --hl-1: var(--dark-hl-1); 34 | --hl-2: var(--dark-hl-2); 35 | --hl-3: var(--dark-hl-3); 36 | --hl-4: var(--dark-hl-4); 37 | --hl-5: var(--dark-hl-5); 38 | --hl-6: var(--dark-hl-6); 39 | --code-background: var(--dark-code-background); 40 | } } 41 | 42 | body.light { 43 | --hl-0: var(--light-hl-0); 44 | --hl-1: var(--light-hl-1); 45 | --hl-2: var(--light-hl-2); 46 | --hl-3: var(--light-hl-3); 47 | --hl-4: var(--light-hl-4); 48 | --hl-5: var(--light-hl-5); 49 | --hl-6: var(--light-hl-6); 50 | --code-background: var(--light-code-background); 51 | } 52 | 53 | body.dark { 54 | --hl-0: var(--dark-hl-0); 55 | --hl-1: var(--dark-hl-1); 56 | --hl-2: var(--dark-hl-2); 57 | --hl-3: var(--dark-hl-3); 58 | --hl-4: var(--dark-hl-4); 59 | --hl-5: var(--dark-hl-5); 60 | --hl-6: var(--dark-hl-6); 61 | --code-background: var(--dark-code-background); 62 | } 63 | 64 | .hl-0 { color: var(--hl-0); } 65 | .hl-1 { color: var(--hl-1); } 66 | .hl-2 { color: var(--hl-2); } 67 | .hl-3 { color: var(--hl-3); } 68 | .hl-4 { color: var(--hl-4); } 69 | .hl-5 { color: var(--hl-5); } 70 | .hl-6 { color: var(--hl-6); } 71 | pre, code { background: var(--code-background); } 72 | -------------------------------------------------------------------------------- /docs/assets/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mondanzo/node-curseforge/9297087785952f3f1e2d2882483c9956883eabce/docs/assets/icons.png -------------------------------------------------------------------------------- /docs/assets/icons@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mondanzo/node-curseforge/9297087785952f3f1e2d2882483c9956883eabce/docs/assets/icons@2x.png -------------------------------------------------------------------------------- /docs/assets/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mondanzo/node-curseforge/9297087785952f3f1e2d2882483c9956883eabce/docs/assets/widgets.png -------------------------------------------------------------------------------- /docs/assets/widgets@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mondanzo/node-curseforge/9297087785952f3f1e2d2882483c9956883eabce/docs/assets/widgets@2x.png -------------------------------------------------------------------------------- /docs/classes/Category.html: -------------------------------------------------------------------------------- 1 | Category | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu
2 |

Represents a Category from Curseforge.

3 |

Hierarchy

  • CFObject
    • Category

Index

Constructors

Properties

classId: number
dateModified: Date
gameId: number
iconUrl: string
id: number
isClass: boolean
name: string
parentCategoryId: number
slug: string
url: string

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/classes/Exceptions.ErrorNotFound.html: -------------------------------------------------------------------------------- 1 | ErrorNotFound | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu
2 |

Error getting thrown if the requested resource could not be found.

3 |

Hierarchy

  • Error
    • ErrorNotFound

Index

Constructors

  • Parameters

    • Optional message: string

    Returns ErrorNotFound

Properties

code: number = 404
message: string = "The requested resource could not be found."
name: string = "Not Found"
stack?: string
prepareStackTrace?: (err: Error, stackTraces: CallSite[]) => any

Type declaration

stackTraceLimit: number

Methods

  • captureStackTrace(targetObject: object, constructorOpt?: Function): void
  • 7 |

    Create .stack property on a target object

    8 |

    Parameters

    • targetObject: object
    • Optional constructorOpt: Function

    Returns void

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/enums/Enums.CoreApiStatus.html: -------------------------------------------------------------------------------- 1 | CoreApiStatus | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu

Enumeration CoreApiStatus

Index

Enumeration members

Enumeration members

PRIVATE = 1
PUBLIC = 2

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/enums/Enums.CoreStatus.html: -------------------------------------------------------------------------------- 1 | CoreStatus | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu

Enumeration CoreStatus

Index

Enumeration members

APPROVED = 5
DRAFT = 1
LIVE = 6
PENDING_REVIEW = 3
REJECTED = 4
TEST = 2

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/enums/Enums.FileHashAlgorithms.html: -------------------------------------------------------------------------------- 1 | FileHashAlgorithms | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu

Enumeration FileHashAlgorithms

Index

Enumeration members

Enumeration members

MD5 = 2
SHA1 = 1

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/enums/Enums.FileRelationType.html: -------------------------------------------------------------------------------- 1 | FileRelationType | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu

Enumeration FileRelationType

Index

Enumeration members

EMBEDDED_LIBRARY = 1
INCLUDE = 6
INCOMPATIBLE = 5
OPTIONAL_DEPENDENCY = 2
REQUIRED_DEPENDENCY = 3
TOOL = 4

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/enums/Enums.FileReleaseType.html: -------------------------------------------------------------------------------- 1 | FileReleaseType | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu

Enumeration FileReleaseType

Index

Enumeration members

Enumeration members

ALPHA = 3
BETA = 2
RELEASE = 1

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/enums/Enums.GameVersionStatus.html: -------------------------------------------------------------------------------- 1 | GameVersionStatus | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu

Enumeration GameVersionStatus

Index

Enumeration members

Enumeration members

APPROVED = 1
DELETED = 2
NEW = 3

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/enums/Enums.GameVersionTypeStatus.html: -------------------------------------------------------------------------------- 1 | GameVersionTypeStatus | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu

Enumeration GameVersionTypeStatus

Index

Enumeration members

Enumeration members

DELETED = 2
NORMAL = 1

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/enums/Enums.ModLoaderInstallMethod.html: -------------------------------------------------------------------------------- 1 | ModLoaderInstallMethod | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu

Enumeration ModLoaderInstallMethod

Index

Enumeration members

FORGE_INSTALLER = 1
FORGE_INSTALLER_V2 = 3
FORGE_JAR_INSTALL = 2

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/enums/Enums.ModLoaderType.html: -------------------------------------------------------------------------------- 1 | ModLoaderType | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu

Enumeration ModLoaderType

Index

Enumeration members

ANY = 0
CAULDRON = 2
FABRIC = 4
FORGE = 1
LITE_LOADER = 3

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/enums/Enums.ModStatus.html: -------------------------------------------------------------------------------- 1 | ModStatus | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu

Enumeration ModStatus

Index

Enumeration members

ABANDONED = 8
APPROVED = 4
CHANGES_MADE = 6
CHANGES_REQUIRED = 2
DELETED = 9
INACTIVE = 7
NEW = 1
REJECTED = 5
UNDER_REVIEW = 10
UNDER_SOFT_REVIEW = 3

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/enums/Enums.ModsSearchSortField.html: -------------------------------------------------------------------------------- 1 | ModsSearchSortField | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu

Enumeration ModsSearchSortField

Index

Enumeration members

AUTHOR = 5
CATEGORY = 7
FEATURED = 1
GAME_VERSION = 8
LAST_UPDATED = 3
NAME = 4
POPULARITY = 2
TOTAL_DOWNLOADS = 6

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu

node-curseforge

2 | 3 |

node-curseforge npm bundle size npm

4 |
5 |

A zero-dependencies NodeJS package to interact with the curseforge core api. 6 | Requires a CFCore authentication token (get one from console.curseforge.com/#/api-keys and yes you need to use a google account to login (sadly)).

7 | 8 | 9 |

view on npm: node-curseforge

10 | 11 | 12 | 13 |

Installation

14 |
15 |

To install this package you can use either yarn or npm.

16 |

yarn add node-curseforge

17 |

or

18 |

npm install node-curseforge

19 |
20 | 21 | 22 |

How-To Use

23 |
24 |

You can use this package mostly in two ways. Either you fetch a game first and interact over the game mostly with the api or you use the root class and give the required ids or objects directly.

25 | 26 | 27 |

Using the Curseforge class

28 |
29 |

const {Curseforge} = require("node-curseforge");

let cf = new Curseforge(cf_token);

let mc = cf.get_game("minecraft");

cf.search_mods(mc).then((mods) => {
for(let mod of mods){
cf.get_description(mod).then((desc) => {
console.log(desc);
});
}
});
30 |
31 |
32 | 33 | 34 |

Using the Curseforge objects

35 |
36 |

Using the methods on the objects themselves actually calls them using the curseforge instance and directly fills in the ids.

37 |

const {Curseforge} = require("node-curseforge")

let cf = new Curseforge(cf_token);

let mc = cf.get_game("minecraft");

mc.search_mods().then((mods) => {
for(let mod of mods){
mod.get_description().then((desc) => {
console.log(desc);
})
}
})
38 |
39 | 40 | 41 |

Typescript Support

42 |
43 |

The entire package is written in typescript and compiled to Javascript with an additional declaration file! You can therefore use this file with regular Javascript or with regular Typescript.

44 | 45 | 46 |

Documentation

47 |
48 |

You can find the (wip) documentation at mondanzo.github.io/node-curseforge. Most of the code is very self-explanatory though and overall also 1:1 identical with the CFCore Docs.

49 |

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/modules.html: -------------------------------------------------------------------------------- 1 | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu

node-curseforge

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/modules/Enums.html: -------------------------------------------------------------------------------- 1 | Enums | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu

Namespace Enums

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/modules/Exceptions.html: -------------------------------------------------------------------------------- 1 | Exceptions | node-curseforge
Options
All
  • Public
  • Public/Protected
  • All
Menu

Namespace Exceptions

Generated using TypeDoc

-------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-curseforge", 3 | "version": "1.2.2", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "node-curseforge", 9 | "version": "1.2.2", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@types/node": "^17.0.15", 13 | "typedoc": "^0.22.11", 14 | "typedoc-plugin-extras": "^2.2.3", 15 | "typedoc-plugin-inline-sources": "^1.0.1", 16 | "typedoc-plugin-rename-defaults": "^0.4.0", 17 | "typescript": "^4.5.5" 18 | } 19 | }, 20 | "node_modules/@types/node": { 21 | "version": "17.0.15", 22 | "dev": true, 23 | "license": "MIT" 24 | }, 25 | "node_modules/balanced-match": { 26 | "version": "1.0.2", 27 | "dev": true, 28 | "license": "MIT" 29 | }, 30 | "node_modules/brace-expansion": { 31 | "version": "1.1.11", 32 | "dev": true, 33 | "license": "MIT", 34 | "dependencies": { 35 | "balanced-match": "^1.0.0", 36 | "concat-map": "0.0.1" 37 | } 38 | }, 39 | "node_modules/concat-map": { 40 | "version": "0.0.1", 41 | "dev": true, 42 | "license": "MIT" 43 | }, 44 | "node_modules/fs.realpath": { 45 | "version": "1.0.0", 46 | "dev": true, 47 | "license": "ISC" 48 | }, 49 | "node_modules/glob": { 50 | "version": "7.2.0", 51 | "dev": true, 52 | "license": "ISC", 53 | "dependencies": { 54 | "fs.realpath": "^1.0.0", 55 | "inflight": "^1.0.4", 56 | "inherits": "2", 57 | "minimatch": "^3.0.4", 58 | "once": "^1.3.0", 59 | "path-is-absolute": "^1.0.0" 60 | }, 61 | "engines": { 62 | "node": "*" 63 | }, 64 | "funding": { 65 | "url": "https://github.com/sponsors/isaacs" 66 | } 67 | }, 68 | "node_modules/inflight": { 69 | "version": "1.0.6", 70 | "dev": true, 71 | "license": "ISC", 72 | "dependencies": { 73 | "once": "^1.3.0", 74 | "wrappy": "1" 75 | } 76 | }, 77 | "node_modules/inherits": { 78 | "version": "2.0.4", 79 | "dev": true, 80 | "license": "ISC" 81 | }, 82 | "node_modules/jsonc-parser": { 83 | "version": "3.0.0", 84 | "dev": true, 85 | "license": "MIT" 86 | }, 87 | "node_modules/lunr": { 88 | "version": "2.3.9", 89 | "dev": true, 90 | "license": "MIT" 91 | }, 92 | "node_modules/marked": { 93 | "version": "4.0.12", 94 | "dev": true, 95 | "license": "MIT", 96 | "bin": { 97 | "marked": "bin/marked.js" 98 | }, 99 | "engines": { 100 | "node": ">= 12" 101 | } 102 | }, 103 | "node_modules/minimatch": { 104 | "version": "3.1.2", 105 | "dev": true, 106 | "license": "ISC", 107 | "dependencies": { 108 | "brace-expansion": "^1.1.7" 109 | }, 110 | "engines": { 111 | "node": "*" 112 | } 113 | }, 114 | "node_modules/once": { 115 | "version": "1.4.0", 116 | "dev": true, 117 | "license": "ISC", 118 | "dependencies": { 119 | "wrappy": "1" 120 | } 121 | }, 122 | "node_modules/path-is-absolute": { 123 | "version": "1.0.1", 124 | "dev": true, 125 | "license": "MIT", 126 | "engines": { 127 | "node": ">=0.10.0" 128 | } 129 | }, 130 | "node_modules/shiki": { 131 | "version": "0.10.1", 132 | "dev": true, 133 | "license": "MIT", 134 | "dependencies": { 135 | "jsonc-parser": "^3.0.0", 136 | "vscode-oniguruma": "^1.6.1", 137 | "vscode-textmate": "5.2.0" 138 | } 139 | }, 140 | "node_modules/typedoc": { 141 | "version": "0.22.11", 142 | "dev": true, 143 | "license": "Apache-2.0", 144 | "dependencies": { 145 | "glob": "^7.2.0", 146 | "lunr": "^2.3.9", 147 | "marked": "^4.0.10", 148 | "minimatch": "^3.0.4", 149 | "shiki": "^0.10.0" 150 | }, 151 | "bin": { 152 | "typedoc": "bin/typedoc" 153 | }, 154 | "engines": { 155 | "node": ">= 12.10.0" 156 | }, 157 | "peerDependencies": { 158 | "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x" 159 | } 160 | }, 161 | "node_modules/typedoc-plugin-extras": { 162 | "version": "2.2.3", 163 | "dev": true, 164 | "license": "MIT", 165 | "peerDependencies": { 166 | "typedoc": "0.22.x" 167 | } 168 | }, 169 | "node_modules/typedoc-plugin-inline-sources": { 170 | "version": "1.0.1", 171 | "dev": true, 172 | "license": "MIT", 173 | "engines": { 174 | "node": ">= 10.8.0" 175 | }, 176 | "peerDependencies": { 177 | "typedoc": ">=0.22.0" 178 | } 179 | }, 180 | "node_modules/typedoc-plugin-rename-defaults": { 181 | "version": "0.4.0", 182 | "dev": true, 183 | "license": "MIT", 184 | "peerDependencies": { 185 | "typedoc": "0.22.x" 186 | } 187 | }, 188 | "node_modules/typescript": { 189 | "version": "4.5.5", 190 | "dev": true, 191 | "license": "Apache-2.0", 192 | "bin": { 193 | "tsc": "bin/tsc", 194 | "tsserver": "bin/tsserver" 195 | }, 196 | "engines": { 197 | "node": ">=4.2.0" 198 | } 199 | }, 200 | "node_modules/vscode-oniguruma": { 201 | "version": "1.6.1", 202 | "dev": true, 203 | "license": "MIT" 204 | }, 205 | "node_modules/vscode-textmate": { 206 | "version": "5.2.0", 207 | "dev": true, 208 | "license": "MIT" 209 | }, 210 | "node_modules/wrappy": { 211 | "version": "1.0.2", 212 | "dev": true, 213 | "license": "ISC" 214 | } 215 | }, 216 | "dependencies": { 217 | "@types/node": { 218 | "version": "17.0.15", 219 | "dev": true 220 | }, 221 | "balanced-match": { 222 | "version": "1.0.2", 223 | "dev": true 224 | }, 225 | "brace-expansion": { 226 | "version": "1.1.11", 227 | "dev": true, 228 | "requires": { 229 | "balanced-match": "^1.0.0", 230 | "concat-map": "0.0.1" 231 | } 232 | }, 233 | "concat-map": { 234 | "version": "0.0.1", 235 | "dev": true 236 | }, 237 | "fs.realpath": { 238 | "version": "1.0.0", 239 | "dev": true 240 | }, 241 | "glob": { 242 | "version": "7.2.0", 243 | "dev": true, 244 | "requires": { 245 | "fs.realpath": "^1.0.0", 246 | "inflight": "^1.0.4", 247 | "inherits": "2", 248 | "minimatch": "^3.0.4", 249 | "once": "^1.3.0", 250 | "path-is-absolute": "^1.0.0" 251 | } 252 | }, 253 | "inflight": { 254 | "version": "1.0.6", 255 | "dev": true, 256 | "requires": { 257 | "once": "^1.3.0", 258 | "wrappy": "1" 259 | } 260 | }, 261 | "inherits": { 262 | "version": "2.0.4", 263 | "dev": true 264 | }, 265 | "jsonc-parser": { 266 | "version": "3.0.0", 267 | "dev": true 268 | }, 269 | "lunr": { 270 | "version": "2.3.9", 271 | "dev": true 272 | }, 273 | "marked": { 274 | "version": "4.0.12", 275 | "dev": true 276 | }, 277 | "minimatch": { 278 | "version": "3.1.2", 279 | "dev": true, 280 | "requires": { 281 | "brace-expansion": "^1.1.7" 282 | } 283 | }, 284 | "once": { 285 | "version": "1.4.0", 286 | "dev": true, 287 | "requires": { 288 | "wrappy": "1" 289 | } 290 | }, 291 | "path-is-absolute": { 292 | "version": "1.0.1", 293 | "dev": true 294 | }, 295 | "shiki": { 296 | "version": "0.10.1", 297 | "dev": true, 298 | "requires": { 299 | "jsonc-parser": "^3.0.0", 300 | "vscode-oniguruma": "^1.6.1", 301 | "vscode-textmate": "5.2.0" 302 | } 303 | }, 304 | "typedoc": { 305 | "version": "0.22.11", 306 | "dev": true, 307 | "requires": { 308 | "glob": "^7.2.0", 309 | "lunr": "^2.3.9", 310 | "marked": "^4.0.10", 311 | "minimatch": "^3.0.4", 312 | "shiki": "^0.10.0" 313 | } 314 | }, 315 | "typedoc-plugin-extras": { 316 | "version": "2.2.3", 317 | "dev": true, 318 | "requires": {} 319 | }, 320 | "typedoc-plugin-inline-sources": { 321 | "version": "1.0.1", 322 | "dev": true, 323 | "requires": {} 324 | }, 325 | "typedoc-plugin-rename-defaults": { 326 | "version": "0.4.0", 327 | "dev": true, 328 | "requires": {} 329 | }, 330 | "typescript": { 331 | "version": "4.5.5", 332 | "dev": true 333 | }, 334 | "vscode-oniguruma": { 335 | "version": "1.6.1", 336 | "dev": true 337 | }, 338 | "vscode-textmate": { 339 | "version": "5.2.0", 340 | "dev": true 341 | }, 342 | "wrappy": { 343 | "version": "1.0.2", 344 | "dev": true 345 | } 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-curseforge", 3 | "version": "1.3.3", 4 | "description": "A NodeJS package for the curseforge api", 5 | "keywords": [ 6 | "curseforge", 7 | "modding", 8 | "mods" 9 | ], 10 | "main": "./dist/index.js", 11 | "repository": "https://github.com/Mondanzo/node-curseforge", 12 | "author": "Mondanzo", 13 | "license": "MIT", 14 | "typings": "./dist/index.d.ts", 15 | "scripts": { 16 | "build-project": "tsc", 17 | "generate-docs": "typedoc", 18 | "test": "echo Here would be tests, if there were any." 19 | }, 20 | "devDependencies": { 21 | "@types/node": "^17.0.15", 22 | "typedoc": "^0.22.11", 23 | "typedoc-plugin-extras": "^2.2.3", 24 | "typedoc-plugin-inline-sources": "^1.0.1", 25 | "typedoc-plugin-rename-defaults": "^0.4.0", 26 | "typescript": "^4.5.5" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/objects/Category.ts: -------------------------------------------------------------------------------- 1 | import Curseforge from ".."; 2 | import { CFObject } from "./interfaces"; 3 | 4 | /** 5 | * Represents a Category from Curseforge. 6 | */ 7 | export default class Category extends CFObject { 8 | public readonly id: number; 9 | public readonly gameId: number; 10 | public readonly name: string; 11 | public readonly slug: string; 12 | public readonly url: string; 13 | public readonly iconUrl: string; 14 | public readonly dateModified: Date; 15 | public readonly isClass: boolean | null; 16 | public readonly classId: number | null; 17 | public readonly parentCategoryId: number | null; 18 | 19 | constructor(_client: Curseforge, data: any) { 20 | super(_client); 21 | 22 | this.id = data.id; 23 | this.gameId = data.gameId; 24 | this.name = data.name; 25 | this.slug = data.slug; 26 | this.url = data.url; 27 | this.dateModified = new Date(data.dateModified); 28 | this.isClass = data.isClass; 29 | this.classId = data.classId; 30 | this.parentCategoryId = data.parentCategoryId; 31 | } 32 | } -------------------------------------------------------------------------------- /src/objects/Game.ts: -------------------------------------------------------------------------------- 1 | import { GameAssets, GameVersionType, Pagination, PagingOptions, SearchOptions } from "./types"; 2 | import { CoreStatus, CoreApiStatus } from "./enums"; 3 | import Curseforge from ".."; 4 | import { CFObject } from "./interfaces"; 5 | import { Mod } from "."; 6 | import { Category } from "."; 7 | import { cleanse } from "../utils"; 8 | 9 | /** 10 | * Represents a specific game from the CF-Core api. 11 | */ 12 | export default class Game extends CFObject { 13 | 14 | /** 15 | * The id of the game. 16 | * @readonly 17 | */ 18 | public readonly id: number; 19 | 20 | /** 21 | * The name of the game. 22 | * @readonly 23 | */ 24 | public readonly name: string; 25 | 26 | /** 27 | * The slug of the game. 28 | * a slug is a string identifier. 29 | * @readonly 30 | */ 31 | public readonly slug: string; 32 | 33 | /** 34 | * the Date when the game got last modified. Does not include changes in the mods. 35 | * @readonly 36 | */ 37 | public readonly dateModified: Date; 38 | 39 | /** 40 | * Assets related to the game. 41 | * @readonly 42 | */ 43 | public readonly assets: GameAssets; 44 | 45 | /** 46 | * The game status. 47 | * @readonly 48 | */ 49 | public readonly status: CoreStatus; 50 | 51 | /** 52 | * The api status for the game. 53 | * @readonly 54 | */ 55 | public readonly apiStatus: CoreApiStatus; 56 | 57 | constructor(_client: Curseforge, data: any) { 58 | super(_client); 59 | 60 | this.id = data.id; 61 | this.name = data.name; 62 | this.slug = data.slug; 63 | this.dateModified = new Date(data.dateModified); 64 | this.assets = data.assets; 65 | this.status = data.status; 66 | this.apiStatus = data.apiStatus; 67 | } 68 | 69 | /** 70 | * Get the categories for this game. 71 | * @param classId Optional TopLevel id of the category to get from. 72 | * @returns a list of categories. 73 | */ 74 | public get_categories(classId?: number | Category): Promise { 75 | return this._client.get_categories(this.id, cleanse(classId)); 76 | } 77 | 78 | /** 79 | * Search for "mods" related to this game. 80 | * Mods also by default includes things like Resource packs / mod packs / custom worlds. Make sure to use the proper Top-Level Category if you only wants to find game modifications. 81 | * @param options Optional Options for searching and Paging. 82 | * @returns a list of found [[Mod]] as well as a paging value for Pagination. 83 | */ 84 | public search_mods(options?: SearchOptions & PagingOptions): Promise { 85 | return this._client.search_mods(this.id, options); 86 | } 87 | 88 | /** 89 | * 90 | * @param gameVersionType Optional game version type to look for. 91 | * @param excludedMods a list of mods to not include. 92 | * @returns an object with multiple lists of [[Mod]] 93 | */ 94 | public get_featured(gameVersionType?: number | GameVersionType, excludedMods?: number[]): Promise<{featured: Mod[], popular: Mod[], recentlyUpdated: Mod[]}> { 95 | return this._client.get_featured_mods(this.id, gameVersionType, excludedMods); 96 | } 97 | } -------------------------------------------------------------------------------- /src/objects/MinecraftModLoader.ts: -------------------------------------------------------------------------------- 1 | import Curseforge from ".."; 2 | import { CFObject } from "./interfaces"; 3 | import { ModLoaderType, ModLoaderInstallMethod, GameVersionStatus, GameVersionTypeStatus } from "./enums"; 4 | 5 | export default class MinecraftModLoader extends CFObject { 6 | public readonly name: string; 7 | public readonly gameVersion?: string; 8 | public readonly gameVersionId?: number; 9 | public readonly forgeVersion?: string; 10 | public readonly latest: boolean; 11 | public readonly recommended: boolean; 12 | public readonly dateModified: Date; 13 | public readonly type: ModLoaderType; 14 | public readonly downloadUrl?: string; 15 | public readonly filename?: string; 16 | public readonly installMethod?: ModLoaderInstallMethod; 17 | public readonly approved?: boolean; 18 | public readonly mavenVersionString?: string; 19 | public readonly versionJson?: string; 20 | public readonly librariesInstallLocation?: string; 21 | public readonly minecraftVersion?: string; 22 | public readonly additionalFilesJson?: string; 23 | public readonly modLoaderGameVersionId?: number; 24 | public readonly modLoaderGameVersionTypeId?: number; 25 | public readonly modLoaderGameVersionStatus?: GameVersionStatus; 26 | public readonly modLoaderGameVersionTypeStatus?: GameVersionTypeStatus; 27 | public readonly mcGameVersionId?: number; 28 | public readonly mcGameVersionTypeId?: number; 29 | public readonly mcGameVersionTypeStatus?: number; 30 | public readonly installProfileJson?: string; 31 | 32 | public constructor(curseforge: Curseforge, data: any) { 33 | super(curseforge); 34 | this.name = data.name; 35 | this.gameVersion = data.gameVersion; 36 | this.gameVersionId = data.gameVersionId; 37 | this.forgeVersion = data.forgeVersion; 38 | this.latest = data.latest; 39 | this.recommended = data.recommended; 40 | this.dateModified = new Date(data.dateModified); 41 | this.type = data.type; 42 | // The official Curseforge API only return a downloadUrl for a Dummy file 43 | this.downloadUrl = `https://maven.minecraftforge.net/net/minecraftforge/forge/${data.minecraftVersion}-${data.forgeVersion}/forge-${data.minecraftVersion}-${data.forgeVersion}-installer.jar` || data.downloadUrl; 44 | this.filename = data.filename; 45 | this.installMethod = data.installMethod; 46 | this.approved = data.approved; 47 | this.mavenVersionString = data.mavenVersionString; 48 | this.versionJson = data.versionJson; 49 | this.librariesInstallLocation = data.librariesInstallLocation; 50 | this.minecraftVersion = data.minecraftVersion; 51 | this.additionalFilesJson = data.additionalFilesJson; 52 | this.modLoaderGameVersionId = data.modLoaderGameVersionId; 53 | this.modLoaderGameVersionTypeId = data.modLoaderGameVersionTypeId; 54 | this.modLoaderGameVersionStatus = data.modLoaderGameVersionStatus; 55 | this.modLoaderGameVersionTypeStatus = data.modLoaderGameVersionTypeStatus; 56 | this.mcGameVersionId = data.mcGameVersionId; 57 | this.mcGameVersionTypeId = data.mcGameVersionTypeId; 58 | this.mcGameVersionTypeStatus = data.mcGameVersionTypeStatus; 59 | this.installProfileJson = data.installProfileJson; 60 | } 61 | } -------------------------------------------------------------------------------- /src/objects/MinecraftVersion.ts: -------------------------------------------------------------------------------- 1 | import Curseforge from ".."; 2 | import { CFObject } from "./interfaces"; 3 | import { GameVersionStatus, GameVersionTypeStatus } from "./enums"; 4 | 5 | export default class MinecraftVersion extends CFObject { 6 | public readonly id: number; 7 | public readonly gameVersionId: number; 8 | public readonly versionString: string; 9 | public readonly jarDownloadUrl: string; 10 | public readonly jsonDownloadUrl: string; 11 | public readonly approved: boolean; 12 | public readonly dateModified: Date; 13 | public readonly gameVersionTypeId: number; 14 | public readonly gameVersionStatus: GameVersionStatus; 15 | public readonly gameVersionTypeStatus: GameVersionTypeStatus; 16 | 17 | public constructor(curseforge: Curseforge, data: any) { 18 | super(curseforge); 19 | this.id = data.id; 20 | this.gameVersionId = data.gameVersionId; 21 | this.versionString = data.versionString; 22 | this.jarDownloadUrl = data.jarDownloadUrl; 23 | this.jsonDownloadUrl = data.jsonDownloadUrl; 24 | this.approved = data.approved; 25 | this.dateModified = new Date(data.dateModified); 26 | this.gameVersionTypeId = data.gameVersionTypeId; 27 | this.gameVersionStatus = data.gameVersionStatus; 28 | this.gameVersionTypeStatus = data.gameVersionTypeStatus; 29 | } 30 | } -------------------------------------------------------------------------------- /src/objects/Mod.ts: -------------------------------------------------------------------------------- 1 | import { Category, ModFile } from "."; 2 | import Curseforge from ".."; 3 | import { ModLoaderType, ModStatus } from "./enums"; 4 | import { CFObject } from "./interfaces"; 5 | import { FileIndex, ModAsset, ModAuthor, ModLinks, Pagination, PagingOptions } from "./types"; 6 | 7 | /** 8 | * Class for working with a specific mod from the CF-Core api. 9 | * Use the Curseforge class to get mod objects. 10 | */ 11 | export default class Mod extends CFObject { 12 | 13 | /** 14 | * the id of the mod. 15 | */ 16 | public readonly id: number; 17 | 18 | /** 19 | * the id of the game of the mod. 20 | */ 21 | public readonly gameId: number; 22 | 23 | /** 24 | * the name of this mod. 25 | */ 26 | public readonly name: string; 27 | 28 | /** 29 | * the slug (string-id) of the mod. 30 | */ 31 | public readonly slug: string; 32 | 33 | /** 34 | * Additional links related to the mod. 35 | */ 36 | public readonly links: ModLinks; 37 | 38 | /** 39 | * A brief summary of the mod. (Think of it as the Github Repo description) 40 | */ 41 | public readonly summary: string; 42 | 43 | /** 44 | * The curseforge approval status of the mod. 45 | */ 46 | public readonly status: ModStatus; 47 | 48 | /** 49 | * The amount this mod got downloaded according to curseforge. 50 | */ 51 | public readonly downloadCount: number; 52 | 53 | /** 54 | * if this mod is a featured mod. 55 | */ 56 | public readonly isFeatured: boolean; 57 | 58 | /** 59 | * The root category id of the mod. (Can be a modification or another associated file with the game like a resource pack or a savegame) 60 | */ 61 | public readonly primaryCategoryId: number; 62 | 63 | /** 64 | * Categories of this mod (also contains root category.) 65 | */ 66 | public readonly categories: Category[]; 67 | 68 | /** 69 | * Authors of this mod. 70 | */ 71 | public readonly authors: ModAuthor[]; 72 | 73 | /** 74 | * The [[ModAsset]] for the logo of this mod. (doesn't need to be in square format) 75 | */ 76 | public readonly logo: ModAsset; 77 | 78 | /** 79 | * Thumbnails / Screenshots uploaded for this mod. 80 | */ 81 | public readonly thumbnails: ModAsset[]; 82 | 83 | /** 84 | * the file id for the main file (latest release) 85 | */ 86 | public readonly mainFileId: number; 87 | 88 | /** 89 | * list of latest files from this mod. 90 | */ 91 | public readonly latestFiles: ModFile[]; 92 | 93 | public readonly latestFilesIndexes: FileIndex[]; 94 | 95 | /** 96 | * the date when the mod page got created. 97 | */ 98 | public readonly dateCreated: Date; 99 | 100 | /** 101 | * the date when the mod page got updated or files got updated. 102 | */ 103 | public readonly dateModified: Date; 104 | 105 | /** 106 | * the date when the mod got released. (not the same as created) 107 | */ 108 | public readonly dateReleased: Date; 109 | 110 | /** 111 | * boolean saying if distribution is allowed or null. 112 | */ 113 | public readonly allowedModDistribution: boolean | null; 114 | 115 | /** @hidden */ 116 | constructor(_client: Curseforge, data: any) { 117 | super(_client); 118 | 119 | this.id = data.id; 120 | this.gameId = data.gameId; 121 | this.name = data.name; 122 | this.slug = data.slug; 123 | this.links = data.links; 124 | this.summary = data.summary; 125 | this.status = data.status; 126 | this.downloadCount = data.downloadCount; 127 | this.isFeatured = data.isFeatured; 128 | this.primaryCategoryId = data.primaryCategoryId; 129 | this.categories = data.categories; 130 | this.authors = data.authors; 131 | this.logo = data.logo; 132 | this.thumbnails = data.thumbnails; 133 | this.mainFileId = data.mainFileId; 134 | this.latestFiles = data.latestFiles.map((file: any) => new ModFile(_client, file)); 135 | this.latestFilesIndexes = data.latestFilesIndexes; 136 | this.dateCreated = new Date(data.dateCreated); 137 | this.dateModified = new Date(data.dateModified); 138 | this.dateReleased = new Date(data.dateReleased); 139 | this.allowedModDistribution = data.allowedModDistribution; 140 | } 141 | 142 | /** 143 | * 144 | * @param searchOptions Options to use when getting the files for this mod. 145 | * @returns mod files found using this method. 146 | */ 147 | public get_files(searchOptions?: {gameVersion?: string, modLoaderType?: ModLoaderType | number, gameVersionTypeId?: number} & PagingOptions): Promise { 148 | return this._client.get_files(this.id, searchOptions); 149 | } 150 | 151 | /** 152 | * Get the description for this mod in html. 153 | * @returns html string. 154 | */ 155 | public get_description(): Promise { 156 | return this._client.get_mod_description(this.id); 157 | } 158 | 159 | /** 160 | * Return a specific file from this mod. 161 | * @param fileId the id of the file to query. 162 | * @returns [[ModFile]] associated for this file or throws an error. 163 | */ 164 | public get_file(fileId: number): Promise { 165 | return this._client.get_file(this, fileId); 166 | } 167 | } -------------------------------------------------------------------------------- /src/objects/ModFile.ts: -------------------------------------------------------------------------------- 1 | import { Mod } from "."; 2 | import Curseforge from ".."; 3 | import { FileHashAlgorithms, FileRelationType, FileReleaseType, FileStatus } from "./enums"; 4 | import { CFObject } from "./interfaces"; 5 | import { FileDependency, FileHash, FileModule, SortableGameVersion } from "./types"; 6 | import * as https from "https"; 7 | import { PathLike, createWriteStream, createReadStream } from "fs"; 8 | import { createHash } from "crypto"; 9 | 10 | /** 11 | * Represents a single CF-Core file for a specific mod. 12 | */ 13 | export default class ModFile extends CFObject { 14 | public readonly id: number; 15 | public readonly gameId: number; 16 | public readonly modId: number; 17 | public readonly isAvailable: boolean; 18 | public readonly displayName: string; 19 | public readonly fileName: string; 20 | public readonly releaseType: FileReleaseType; 21 | public readonly fileStatus: FileStatus; 22 | public readonly hashes: FileHash[]; 23 | public readonly fileDate: Date; 24 | public readonly fileLength: bigint; 25 | public readonly downloadCount: bigint; 26 | public readonly downloadUrl: string; 27 | public readonly gameVersions: string[]; 28 | public readonly sortableGameVersions: SortableGameVersion[]; 29 | public readonly dependencies: FileDependency[]; 30 | public readonly exposeAsAlternative: boolean | null; 31 | public readonly parentProjectFileId: number | null; 32 | public readonly alternateFileId: number | null; 33 | public readonly isServerPack: boolean | null; 34 | public readonly serverPackFileId: number | null; 35 | public readonly fileFingerprint: bigint; 36 | public readonly modules: FileModule[]; 37 | 38 | constructor(_client: Curseforge, data: any) { 39 | super(_client); 40 | 41 | this.id = data.id; 42 | this.gameId = data.gameId; 43 | this.modId = data.modId; 44 | this.isAvailable = data.isAvailable; 45 | this.displayName = data.displayName; 46 | this.fileName = data.fileName; 47 | this.releaseType = data.releaseType; 48 | this.fileStatus = data.fileStatus; 49 | this.hashes = data.hashes; 50 | this.fileDate = new Date(data.fileDate); 51 | this.fileLength = data.fileLength; 52 | this.downloadCount = data.downloadCount; 53 | this.downloadUrl = data.downloadUrl; 54 | this.gameVersions = data.gameVersions; 55 | this.sortableGameVersions = data.sortableGameVersions; 56 | this.dependencies = data.dependencies; 57 | this.exposeAsAlternative = data.exposeAsAlternative; 58 | this.parentProjectFileId = data.parentProjectFileId; 59 | this.alternateFileId = data.alternateFileId; 60 | this.isServerPack = data.isServerPack; 61 | this.serverPackFileId = data.serverPackFileId; 62 | this.fileFingerprint = data.fileFingerprint; 63 | this.modules = data.modules; 64 | } 65 | 66 | /** 67 | * @hidden 68 | */ 69 | private _download(url: string | URL, path: PathLike, verify: boolean): Promise { 70 | return new Promise((resolve, reject) => { 71 | let req = https.get(url); 72 | req.on("response", (res) => { 73 | if(res.statusCode == 200){ 74 | 75 | let hash: undefined | FileHash = undefined; 76 | 77 | if(verify){ 78 | hash = this.hashes.find((hash) => hash.algo == FileHashAlgorithms.SHA1); 79 | 80 | if(!hash) hash = this.hashes.find((hash) => hash.algo == FileHashAlgorithms.MD5); 81 | 82 | 83 | // No hash available. Can't verify. Sad. 84 | if(!hash) verify = false; 85 | } 86 | 87 | // File exists! 88 | let stream = createWriteStream(path); 89 | 90 | res.on("end", () => { 91 | stream.close(); 92 | if(hash) { 93 | let fileStream = createReadStream(path); 94 | let h = createHash(hash.algo == FileHashAlgorithms.SHA1 ? "sha1" : "md5"); 95 | 96 | fileStream.on("end", () => { 97 | fileStream.close(); 98 | let hashRes = h.digest("hex"); 99 | resolve(hashRes == hash.value); 100 | }); 101 | 102 | fileStream.pipe(h); 103 | } else { 104 | resolve(true); 105 | } 106 | }); 107 | 108 | res.pipe(stream); 109 | } else if (res.statusCode == 302) { 110 | resolve(this._download(res.headers.location, path, verify)); 111 | } else { 112 | reject("Error! No idea what happened"); 113 | } 114 | }); 115 | 116 | req.end(); 117 | }); 118 | } 119 | 120 | /** 121 | * Download this [[ModFile]]. 122 | * @param path The path where the file should be saved. 123 | * @param verify Should the downloaded files hash be checked? 124 | * @returns the Promise resolves with true if download was successful and the hash fits (if verify is true.) returns false otherwise. 125 | */ 126 | public download(path: PathLike, verify: boolean = true): Promise { 127 | return this._download(this.downloadUrl, path, verify); 128 | } 129 | 130 | 131 | /** 132 | * Get the mod associated with this mod file. 133 | * @returns the mod for this mod file. 134 | */ 135 | public get_mod(): Promise { 136 | return this._client.get_mod(this.modId); 137 | } 138 | 139 | /** 140 | * 141 | * @param relations The types to get. This is a filter list to make sure that only requested dependencies are downloaded. 142 | * @returns a list of [[ModFile]] which got found as dependencies. Can be an empty array. 143 | */ 144 | public get_dependencies(relations: FileRelationType[] = [FileRelationType.REQUIRED_DEPENDENCY]): Promise { 145 | return new Promise(async (resolve, reject) => { 146 | let mods = []; 147 | for(let dependency of this.dependencies){ 148 | if(relations.includes(dependency.relationType)){ 149 | mods.push(await this._client.get_file(dependency.modId, dependency.fileId)); 150 | } 151 | } 152 | 153 | resolve(mods); 154 | }); 155 | } 156 | 157 | /** 158 | * Get the changelog for this specific [[ModFile.]] 159 | * @returns The changelog in html. 160 | */ 161 | public get_changelog(): Promise { 162 | return this._client.get_file_changelog(this.modId, this.id); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/objects/enums.ts: -------------------------------------------------------------------------------- 1 | export enum CoreStatus { 2 | DRAFT = 1, 3 | TEST = 2, 4 | PENDING_REVIEW = 3, 5 | REJECTED = 4, 6 | APPROVED = 5, 7 | LIVE = 6 8 | } 9 | 10 | export enum CoreApiStatus { 11 | PRIVATE = 1, 12 | PUBLIC = 2 13 | } 14 | 15 | export enum FileReleaseType { 16 | RELEASE = 1, 17 | BETA = 2, 18 | ALPHA = 3 19 | } 20 | 21 | export enum GameVersionStatus { 22 | APPROVED = 1, 23 | DELETED = 2, 24 | NEW = 3, 25 | } 26 | 27 | export enum GameVersionTypeStatus { 28 | NORMAL = 1, 29 | DELETED = 2, 30 | } 31 | 32 | export enum ModLoaderInstallMethod { 33 | FORGE_INSTALLER = 1, 34 | FORGE_JAR_INSTALL = 2, 35 | FORGE_INSTALLER_V2 = 3 36 | } 37 | 38 | export enum FileStatus { 39 | PROCESSING = 1, 40 | CHANGES_REQUIRED = 2, 41 | UNDER_REVIEW = 3, 42 | APPROVED = 4, 43 | REJECTED = 5, 44 | MALWARE_DETECTED = 6, 45 | DELETED = 7, 46 | ARCHIVED = 8, 47 | TESTING = 9, 48 | RELEASED = 10, 49 | READY_FOR_REVIEW = 11, 50 | DEPRECATED = 12, 51 | BAKING = 13, 52 | AWAITING_PUBLISHING = 14, 53 | FAILED_PUBLISHING = 155 54 | } 55 | 56 | export enum FileRelationType { 57 | EMBEDDED_LIBRARY = 1, 58 | OPTIONAL_DEPENDENCY = 2, 59 | REQUIRED_DEPENDENCY = 3, 60 | TOOL = 4, 61 | INCOMPATIBLE = 5, 62 | INCLUDE = 6 63 | } 64 | 65 | export enum ModsSearchSortField { 66 | FEATURED = 1, 67 | POPULARITY = 2, 68 | LAST_UPDATED = 3, 69 | NAME = 4, 70 | AUTHOR = 5, 71 | TOTAL_DOWNLOADS = 6, 72 | CATEGORY = 7, 73 | GAME_VERSION = 8 74 | } 75 | 76 | export enum ModStatus { 77 | NEW = 1, 78 | CHANGES_REQUIRED = 2, 79 | UNDER_SOFT_REVIEW = 3, 80 | APPROVED = 4, 81 | REJECTED = 5, 82 | CHANGES_MADE = 6, 83 | INACTIVE = 7, 84 | ABANDONED = 8, 85 | DELETED = 9, 86 | UNDER_REVIEW = 10 87 | } 88 | 89 | export enum ModLoaderType { 90 | ANY = 0, 91 | FORGE = 1, 92 | CAULDRON = 2, 93 | LITE_LOADER = 3, 94 | FABRIC = 4 95 | } 96 | 97 | export enum FileHashAlgorithms { 98 | SHA1 = 1, 99 | MD5 = 2 100 | } -------------------------------------------------------------------------------- /src/objects/exceptions.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Error getting thrown if the requested resource could not be found. 4 | */ 5 | export class ErrorNotFound extends Error { name = "Not Found"; message = "The requested resource could not be found."; code = 404 } 6 | /** 7 | * Error getting thrown if the request contains a malformed request body. 8 | */ 9 | export class ErrorBadRequest extends Error { name = "Bad Request"; message = "The request is malformed and won't be processed."; code = 400 } 10 | 11 | /** 12 | * Error getting thrown if the server processing request throws an error. This is not a client error. 13 | */ 14 | export class ErrorInternalServer extends Error { name = "Internal Server Error"; message = "Something on the server happened that made it impossible to resolve the request."; code = 500 } 15 | 16 | /** 17 | * Error getting thrown if the service isn't available at the current time. 18 | */ 19 | export class ErrorServiceUnavailable extends Error { name = "Service Unavailable"; message = "The service currently is not available."; code = 503 } -------------------------------------------------------------------------------- /src/objects/index.ts: -------------------------------------------------------------------------------- 1 | import * as types from "./types"; 2 | import * as enums from "./enums"; 3 | import * as exceptions from "./exceptions"; 4 | 5 | import Game from "./Game"; 6 | import ModFile from "./ModFile"; 7 | import Mod from "./Mod"; 8 | import Category from "./Category"; 9 | import MinecraftModLoader from "./MinecraftModLoader"; 10 | import MinecraftVersion from "./MinecraftVersion"; 11 | 12 | export {Game, ModFile, Mod, Category, types, enums, exceptions, MinecraftModLoader, MinecraftVersion}; -------------------------------------------------------------------------------- /src/objects/interfaces.ts: -------------------------------------------------------------------------------- 1 | import Curseforge from ".."; 2 | 3 | /** 4 | * @hidden 5 | */ 6 | export abstract class CFObject { 7 | 8 | /** 9 | * @hidden 10 | */ 11 | _client: Curseforge; 12 | constructor(_client: Curseforge) { 13 | this._client = _client; 14 | }; 15 | } -------------------------------------------------------------------------------- /src/objects/types.ts: -------------------------------------------------------------------------------- 1 | import { ModFile, Category } from "."; 2 | import { FileHashAlgorithms, FileRelationType, FileReleaseType, ModLoaderType, ModsSearchSortField } from "./enums"; 3 | 4 | export type GameAssets = { 5 | iconUrl: string; 6 | tileUrl: string; 7 | coverUrl: string; 8 | } 9 | 10 | export type Pagination = { 11 | index: number, 12 | /** 13 | * Maximum allowed PageSize is 50. 14 | */ 15 | pageSize: number, 16 | resultCount: number, 17 | totalCount: number 18 | } 19 | 20 | export type FileHash = { 21 | value: string, 22 | algo: FileHashAlgorithms 23 | } 24 | 25 | export type SortableGameVersion = { 26 | gameVersionName: string, 27 | gameVersionPadded: string, 28 | gameVersion: string, 29 | gameVersionReleaseDate: Date, 30 | gameVersionTypeId: number | null 31 | } 32 | 33 | export type FileIndex = { 34 | gameVersion: string, 35 | fileId: number, 36 | filename: string, 37 | releaseType: FileReleaseType, 38 | gameVersionTypeId: number | null, 39 | modLoader: ModLoaderType | null 40 | } 41 | 42 | export type FileDependency = { 43 | modId: number, 44 | fileId: number, 45 | relationType: FileRelationType 46 | } 47 | 48 | export type FileModule = { 49 | name: string, 50 | fingerprint: bigint 51 | } 52 | 53 | export type PagingOptions = { 54 | index?: number, 55 | pageSize?: number 56 | } 57 | 58 | export type SearchOptions = { 59 | classId?: number | Category, 60 | categoryId?: number | Category, 61 | gameVersion?: string, 62 | searchFilter?: string, 63 | sortField?: ModsSearchSortField, 64 | sortOrder?: "asc" | "desc", 65 | modLoaderType?: string, 66 | gameVersionTypeId?: number 67 | } 68 | 69 | export type ModLinks = { 70 | websiteUrl: string, 71 | wikiUrl: string, 72 | issuesUrl: string, 73 | sourceUrl: string 74 | } 75 | 76 | export type ModAuthor = { 77 | id: number, 78 | name: string, 79 | url: string 80 | } 81 | 82 | export type ModAsset = { 83 | id: number, 84 | modId: number, 85 | title: string, 86 | description: string, 87 | thumbnailUrl: string, 88 | url: string 89 | } 90 | 91 | export type GameVersionsByType = { 92 | type: number, 93 | versions: string[] 94 | } 95 | 96 | /** 97 | * the game version type (Retail / Console / etc.) 98 | */ 99 | export type GameVersionType = { 100 | id: number, 101 | gameId: number, 102 | name: string, 103 | slug: string 104 | } 105 | 106 | export type FingerprintMatch = { 107 | id: number, 108 | file: ModFile, 109 | latestFiles: ModFile[] 110 | } 111 | 112 | export type FingerprintMatchResult = { 113 | isCacheBuilt: boolean, 114 | exactMatches: FingerprintMatch[], 115 | exactFingerprints: number[], 116 | partialMatches: FingerprintMatch[], 117 | partialMatchFingerprints: object, 118 | additionalProperties?: number[], 119 | installedFingerprints: number[], 120 | unmatchedFingerprints: number[] 121 | } 122 | 123 | export type FingerprintFuzzyMatch = { 124 | id: number, 125 | file: ModFile, 126 | latestFiles: ModFile[], 127 | fingerprints: number[] 128 | } 129 | 130 | export type FolderFingerprints = { 131 | foldername: string, 132 | fingerprints: number[] 133 | } -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import {request} from "https"; 2 | import { IncomingMessage, OutgoingHttpHeaders } from "http"; 3 | import { URL } from "url"; 4 | import { Game } from "./objects"; 5 | import { CFObject } from "./objects/interfaces"; 6 | import { Pagination } from "./objects/types"; 7 | 8 | export type RequestResponse = {code: number, data?: {"data": any, "pagination"?: Pagination}}; 9 | export type AsyncRequestResponse = Promise; 10 | 11 | var https_headers: OutgoingHttpHeaders; 12 | 13 | export function set_option(header: string) { 14 | https_headers = { 15 | "x-api-key": header 16 | } 17 | } 18 | 19 | /** 20 | * @hidden 21 | * @param method - http method to use for request. 22 | * @param url - url to request. 23 | * @param body - Optional request data to send along. 24 | */ 25 | function rq(method: "GET" | "POST", url: string, body?: object | string | number): AsyncRequestResponse { 26 | return new Promise(function (resolve, reject) { 27 | let body_headers = {}; 28 | let body_data = body ? JSON.stringify(body) : null; 29 | 30 | if(body_data) { 31 | body_headers = { 32 | "Content-Type": "application/json", 33 | "Content-Length": Buffer.byteLength(body_data) 34 | } 35 | } 36 | 37 | let req = request(url, { 38 | method: method, 39 | headers: { 40 | "Accept": "application/json", 41 | ...https_headers, 42 | ...body_headers 43 | } 44 | }); 45 | 46 | 47 | req.on("response", (res: IncomingMessage) => { 48 | let data = ""; 49 | res.on("data", (chunk) => {data += chunk}); 50 | res.on("end", () => { 51 | if(data == "" || data == undefined) resolve({"code": res.statusCode}) 52 | else 53 | resolve({ 54 | "code": res.statusCode, 55 | "data": JSON.parse(data) 56 | }); 57 | }); 58 | }); 59 | 60 | if (body_data) { req.write(body_data) } 61 | 62 | req.end(); 63 | }); 64 | } 65 | 66 | /** 67 | * @hidden 68 | */ 69 | export function get(url: string, body?: any): AsyncRequestResponse { 70 | return rq("GET", url, body); 71 | } 72 | 73 | /** 74 | * @hidden 75 | */ 76 | export function post(url: string, body?: any): AsyncRequestResponse { 77 | return rq("POST", url, body); 78 | } 79 | 80 | /** 81 | * @hidden 82 | */ 83 | export function cleanse(object: CFObject & {id: number} | number): number { 84 | return object instanceof CFObject ? object.id : object; 85 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | "target": "es5", 5 | "module": "CommonJS", 6 | "noImplicitAny": false, 7 | "preserveConstEnums": true, 8 | "sourceMap": true, 9 | "outDir": "dist", 10 | "emitDeclarationOnly": false, 11 | "declaration": true, 12 | "declarationMap": true, 13 | "sourceRoot": "src" 14 | }, 15 | "include": ["src/**/*"] 16 | } -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["src/index.ts"], 3 | "out": "docs" 4 | } --------------------------------------------------------------------------------