Skip to content

Commit

Permalink
Merge pull request #885 from neet/v6
Browse files Browse the repository at this point in the history
Masto.js v6
  • Loading branch information
neet committed Jul 27, 2023
2 parents 412a046 + bc3d297 commit 95c121e
Show file tree
Hide file tree
Showing 459 changed files with 9,748 additions and 9,097 deletions.
4 changes: 2 additions & 2 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
version: '2'
version: "2"
checks:
file-lines:
enabled: false
Expand All @@ -14,4 +14,4 @@ checks:
config:
threshold: 10
exclude_patterns:
- '**/__tests__/'
- "**/__tests__/"
34 changes: 24 additions & 10 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,14 @@
"plugin:prettier/recommended"
],
"plugins": ["import", "simple-import-sort"],
"parserOptions": {
"ecmaVersion": 9,
"sourceType": "module"
},
"env": {
"browser": true,
"node": true,
"jest": true
"node": true
},
"rules": {
"no-console": "error",
"no-unused-vars": "off",
"import/no-cycle": "error",
"simple-import-sort/imports": "error",
"unicorn/prevent-abbreviations": "off",
"unicorn/no-array-reduce": "off"
Expand All @@ -26,11 +23,15 @@
{
"files": "**/*.ts",
"extends": [
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/strict",
"plugin:import/typescript",
"plugin:prettier/recommended"
],
"parserOptions": {
"ecmaVersion": 9,
"sourceType": "module",
"project": "./tsconfig.json"
},
"rules": {
"@typescript-eslint/explicit-module-boundary-types": "error",
"@typescript-eslint/explicit-member-accessibility": [
Expand All @@ -41,23 +42,36 @@
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_"
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_"
}
],
"@typescript-eslint/consistent-type-imports": [
"error",
{ "prefer": "type-imports" }
{ "prefer": "type-imports", "fixStyle": "inline-type-imports" }
]
}
},
{
"files": "examples/**",
"parserOptions": {
"project": "./examples/tsconfig.json"
},
"rules": {
"no-console": "off",
"import/no-unresolved": "off",
"unicorn/prefer-top-level-await": "off",
"unicorn/no-process-exit": "off"
}
},
{
"files": ["tests/**/*.ts", "test-utils/**/*.ts", "**/*.spec.ts"],
"env": {
"jest": true
},
"rules": {
"no-constant-condition": "off"
}
}
]
}
8 changes: 4 additions & 4 deletions .github/workflows/ci-e2e.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
name: 'CI: E2E'
name: "CI: E2E"

on:
push:
branches:
- main
pull_request:
branches:
- '*'
- "*"
workflow_call:
workflow_dispatch:

jobs:
test:
name: End-to-end Testing
name: Test
runs-on: ubuntu-latest
env:
DB_USER: mastodon
Expand Down Expand Up @@ -84,7 +84,7 @@ jobs:
run: yarn install --frozen-lockfile

- name: Run tests
run: yarn run test:e2e
run: yarn run test:e2e --max-workers=1

- name: Codecov
uses: codecov/codecov-action@v3
Expand Down
5 changes: 1 addition & 4 deletions .github/workflows/ci-unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ jobs:
test:
name: Test
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18]

steps:
- uses: actions/checkout@v3
Expand All @@ -26,7 +23,7 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
node-version: 18
cache: yarn

- name: Install dependencies
Expand Down
1 change: 0 additions & 1 deletion .prettierrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"semi": true,
"singleQuote": true,
"trailingComma": "all"
}
2 changes: 1 addition & 1 deletion .releaserc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"branches": ["main", "add-tests-for-websocket"],
"branches": ["main"],
"preset": "conventionalcommits",
"plugins": [
[
Expand Down
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
},
"npm.packageManager": "yarn",
"eslint.packageManager": "yarn",
"jest.jestCommandLine": "jest --runInBand",
"typescript.tsdk": "node_modules/typescript/lib"
}
88 changes: 43 additions & 45 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,58 +1,56 @@
# Contribution Guideline

Thank you for considering contribution. Please check following guideline and please make sure that you satisfy the policy.
## Introduction

## Setup
Thank you for considering contributions. This document describes the technology used in Masto.js and some useful information.

1. Install **Node.js**, **Git** and **Yarn**.
2. Clone this repository by `git clone`
3. Run `yarn install` to install dependencies
I will assume that you have basic TypeScript knowledge, so if you want to learn from the basics, I recommend that you refer to the official TypeScript or Node.js documentation.

## Project Structure
## Technology and Tips

Our project in organized under the following directory structure.
### Mastodon Types and Proxy API

```
./src
├── mastodon TypeScript representation of Mastodon API.
│ │ This directory is public under the namespace `mastodon` and does not contain library-specific code
│ ├── v1
│ │ ├── entities V1 response types
│ │ └── repositories V1 resource classes
│ └── v2
│ ├── entities V2 response types
│ └── repositories V2 resource classes
├── errors Error classes
├── http HTTP wrapper
├── logger Logging service
├── serializers Service to encode requests or decode responses
├── utils General utilities
└── ws Websocket wrapper
```
In principle, Masto.js contains as little runtime code as possible in order to reduce bundle size.

All accesses to the Mastodon API (`masto.v1.statuses.create`, `masto.v1.accounts.$select`, etc.) are interpreted by a [Proxy object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) and converted into an abstract object called `Action`.

The `Action` is converted to the corresponding URL in the layer described below, and the request is sent.

### Architecture

The architecture follows the class diagram below. In principle, interfaces and implementations are separated, with interfaces placed under `src/interfaces` and implementations placed in `src/adapters`.

### Repository
[![](https://mermaid.ink/img/pako:eNqlVU1v4jAQ_SvIp5YC4qMhbIQqVUWr7aGrajlUWuVinAGsJnZqTxAU8d_XSWCbDyeLtLkkmryZNzNvxj4SJgMgHmEh1XrB6UbRyBcd82SWzhIUpyH_BNU55vb0mc-5QFBryuDh4ct8py_om9uCNYCa_VTkeGTIpVhwHVNk2-uYgjP6hmbOt95O8sAS-w1WS8neAZ-kWPPNNaEVaBnu4JXitlTGBvCF7h8RIYpRWyv5gRj_P1EEagO_4CMBjc-C4xvH7QLWNAlbaK8hNBWUiGKpK4aspWXtQkC7bMXWCmAorxKOso-Eq_KAsFDqf3Ask5Vmisep2EWaux0NTZ9K4RKhc_SqGNQ-1T8p8h08R3FYn8hXJfeHElm1gTSOw0OjJNW5rv63c_8tuc293vs8TI4pLO2832-pNhucFGJLx6JvCm2ibnDp97t2FXOP2u5fsrFXb4W3dOyrzJpn13gWdC40pNuaQiObPaKlJd32nCsKdhvkKZw1TZBKlGuVu4RsH7J8EJ9CDgK_0_RveVeYAor1BSyu1nwwKAdoalnK24QuF96Esu2ADesL0iPmAI4oD8y1mBXkE9xCBD7xzGdA1btPfHEyOJqgXB4EIx6qBHokiQNT8vkWJd6ahtpYYyqIdyR74o2mo4F77w4njvNtNnSGbo8ciDd2p4PZaOaMncnMcd3RxDn1yKeUJsJw4M6G4-nY-Izvncl0NOkRCLhJ9eV8baevjOJ35pDmcfoDWe-DdQ?type=png)](https://mermaid.live/edit#pako:eNqlVU1v4jAQ_SvIp5YC4qMhbIQqVUWr7aGrajlUWuVinAGsJnZqTxAU8d_XSWCbDyeLtLkkmryZNzNvxj4SJgMgHmEh1XrB6UbRyBcd82SWzhIUpyH_BNU55vb0mc-5QFBryuDh4ct8py_om9uCNYCa_VTkeGTIpVhwHVNk2-uYgjP6hmbOt95O8sAS-w1WS8neAZ-kWPPNNaEVaBnu4JXitlTGBvCF7h8RIYpRWyv5gRj_P1EEagO_4CMBjc-C4xvH7QLWNAlbaK8hNBWUiGKpK4aspWXtQkC7bMXWCmAorxKOso-Eq_KAsFDqf3Ask5Vmisep2EWaux0NTZ9K4RKhc_SqGNQ-1T8p8h08R3FYn8hXJfeHElm1gTSOw0OjJNW5rv63c_8tuc293vs8TI4pLO2832-pNhucFGJLx6JvCm2ibnDp97t2FXOP2u5fsrFXb4W3dOyrzJpn13gWdC40pNuaQiObPaKlJd32nCsKdhvkKZw1TZBKlGuVu4RsH7J8EJ9CDgK_0_RveVeYAor1BSyu1nwwKAdoalnK24QuF96Esu2ADesL0iPmAI4oD8y1mBXkE9xCBD7xzGdA1btPfHEyOJqgXB4EIx6qBHokiQNT8vkWJd6ahtpYYyqIdyR74o2mo4F77w4njvNtNnSGbo8ciDd2p4PZaOaMncnMcd3RxDn1yKeUJsJw4M6G4-nY-Izvncl0NOkRCLhJ9eV8baevjOJ35pDmcfoDWe-DdQ)

_Repository_ is a class for representing REST resources. They have several methods and multiple implementations, all named according to the following convention. Let `x` is the name of a resource.
The main classes are described as follows

| URL Pattern | Method Name | Parameter Name |
| ------------------------------ | ---------------- | ----------------- |
| `GET /api/v1/x` | `v1.x.list` | `ListXParams` |
| `GET /api/v1/x/:id` | `v1.x.fetch` | `FetchXParams` |
| `POST /api/v1/x` | `v1.x.create` | `CreateXParams` |
| `POST /api/v1/x/:id/{verb}` | `v1.x.do` (verb) | `DoXParams` |
| `GET /api/v1/x/:id/{sub}` | `v1.x.listSub` | `ListXSubParams` |
| `GET /api/v1/x/:id/{sub}/:id2` | `v1.x.fetchSub` | `FetchXSubParams` |
| `DELETE /api/v1/x/:id` | `v1.x.remove` | `RemoveXParams` |
| `PUT or PATCH /api/v1/x/:id` | `v1.x.update` | `UpdateXParams` |
- `Serializer` - A class that converts from JavaScript objects to formats such as JSON and FormData, and vice versa. In addition, the conversion from SnakeCase to CamelCase is performed here.
- `ActionDispatcher` - In Masto.js, streaming subscriptions, HTTP requests, etc. are treated as abstract `Action` data structures, from which `ActionDispatchers` make the actual request.
- `Http` - The class that creates the HTTP communication, used to implement `mastodon.rest.Client`.
- `WebSocketConnector`: class for creating WebSocket instances, used in the implementation of `mastodon.streaming`.

### Testing

Masto.js uses both E2E tests and unit tests. In principle, we write mainly E2E tests, and use unit tests to cover only those areas that cannot be covered, such as exception handling.

In the Jest environment for E2E testing, an object called `session` is exposed in the global space, and you can easily obtain a test account by doing the following.

```typescript
// To simulate a single user
sessions.use(async session => {
await session.rest.v1.statuses.$select("123").fetch();
});

// To simulate an interaction between two users
sessions.use(2, async ([alice, bob]) => {
await alice.rest.v1.statuses.create({
`Hello @${bob.account.acct}`
})
});
```
## Scripts
The tests are automatically run by CI, but if you need to check them locally, please refer to the GitHub Actions workflow in the repository to set up your environment. For other ways to write tests, please refer to the official Jest documentation.
There are some useful scripts in `package.json` which you can with `yarn run`.
## Review and Releases
- `test:jest` - Jest unit testing
- `test:eslint` - Lints the codes with ESLint (includes Prettier)
- `test:spellcheck` - Lints the codes with CSpell
- `test` - Run all tests
- `build` - Build the source code with Rollup
- `prepublishOnly` - Automatically runs before `npm publish`
- `docs:build` - Generate documentation by TypeDoc
Once you have verified your changes locally, create a Pull Request and have it reviewed by the author. If your changes are merged, they will be automatically released by the following 0:00 UTC.
69 changes: 28 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<img src="https://i.imgur.com/jakvzSd.png" width="260px">
</p>

<p align="center">Mastodon API client for JavaScript, TypeScript, Node.js, browsers</p>
<p align="center">Universal Mastodon API client for JavaScript</p>

<p align="center">
<a href="https://www.npmjs.com/package/masto"><img src="https://img.shields.io/npm/v/masto.svg" alt="npm"/></a>
Expand All @@ -12,33 +12,42 @@
</p>

<p align="center">
<a href="https://github.com/neet/masto.js/discussions">Q&A</a> |
<a href="https://github.com/neet/masto.js/tree/main/examples">Examples</a> |
<a href="https://neet.github.io/masto.js">Read the Docs</a> |
<a href="https://github.com/neet/masto.js/releases">Releases</a> |
<a href="https://github.com/neet/masto.js/issues">Issues</a>
<a href="https://github.com/neet/masto.js/releases">Releases</a>
</p>

> [_Migration Guide From v4_](https://github.com/neet/masto.js/releases/tag/v5.0.0)
## Features

- 🌎 **Isomorphic** which means browsers and Node.js are both supported
- 🌊 **Fetch API** is supported natively
- ⌨️ **TypeScript** powers static typing. And of course there's no `any`!
- 💪 **You don't need to type URLs** because each endpoints have their own function
- 📄 **Detailed docs** and rich hovering menu for IDE, provided by TSDoc
- 🌎 **Universal:** Works in Node.js, browsers, and Deno
- 📦 **Lightweight:** Less runtime codes, [7kB+ minified and gzipped](https://bundlephobia.com/package/masto@6.0.0-alpha.7)
- 📚 **TypeScript:** Written in TypeScript, and provides type definitions
- 🌊 **Latest APIs:** Catches up the latest JS features including `fetch`, `AsyncIterator`.
- 🤓 **Maintained:** Actively maintained by a Fediverse lover [since 2018](https://github.com/neet/masto.js/releases/tag/1.0.0)

## Migration Guides

- [v5.x → v6.0.0](https://github.com/neet/masto.js/releases/tag/v6.0.0)
- [v4.x → v5.0.0](https://github.com/neet/masto.js/releases/tag/v5.0.0)

## Who's using Masto.js?

## Quick start
- [Elk](https://github.com/elk-zone/elk) - A nimble Mastodon web client
- [Phanpy](https://github.com/cheeaun/phanpy) - A minimalistic opinionated Mastodon web client
- [...and a lot more!](https://github.com/neet/masto.js/network/dependents)

## Quick Start

In this quick start, we'll take a look at how to create a simple Mastodon bot that publishes a post using _Masto.js_.

Firstly, you need to install _Node.js_ and _npm_ in your environment. Follow [the npm official guide](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) for the setup, and proceed to the next step when it's ready. Alternatively, you can use _yarn_, _pnpm_ or other package managers to install Masto.js, but this guide below uses _npm_.

The minimal required version of dependency is as follows

- **Node.js**: `>= 14.x`
- **npm**: `>= 6.x`
- **TypeScript** (optional peer dependency): `>= 3.6.0`
- **Node.js**: `>= 18.x`
- **npm**: `>= 9.x`
- **TypeScript** (optional peer dependency): `>= 5.0.0`

If you could successfully installed _Node.js_ and _npm_, create your first _Masto.js_ project with the following command. Assume you're using POSIX-compatible operating system.

Expand All @@ -56,7 +65,7 @@ And install Masto.js using _npm_
npm install masto
```

Now you could initialise your project for developing a Mastodon bot. Next, you need to create an application to obtain an _[access token](https://docs.joinmastodon.org/client/authorized/)_ required to get access to your account.
Now you successfully initialised your project for developing a Mastodon bot. Next, you need to create an application to obtain an _[access token](https://docs.joinmastodon.org/client/authorized/)_ required to get access to your account.

Go to your settings page, open **Development**, and click the **New Application** button to earn your personal access token.

Expand All @@ -69,16 +78,16 @@ If you could create an application, save **Your access token** securely. This st
Then you're almost there! Create a file named `index.js` inside your project directory and add the following code. This is an example which will post a status from your account.

```ts
import { login } from 'masto';
import { createRestAPIClient } from "masto";

const masto = await login({
const masto = await createRestAPIClient({
url: process.env.URL,
accessToken: process.env.TOKEN,
});

const status = await masto.v1.statuses.create({
status: 'Hello from #mastojs!',
visibility: 'public',
status: "Hello from #mastojs!",
visibility: "public",
});

console.log(status.url);
Expand All @@ -92,28 +101,6 @@ URL={URL} TOKEN={TOKEN} node ./index.js

Other available features are described in the [documentation](https://neet.github.io/masto.js). You may also want to refer [/examples](https://github.com/neet/masto.js/tree/main/examples) directory on this repository.

## FAQ

### Q. I want to use in Mastodon-compatible servers

Masto.js validates your Mastodon instance's version to provide more helpful error messages, but this may lead to not working with some Mastodon-compatible software. Therefore, we are offering a way to disable this feature.

```diff
await login({
url: "https://example.com",
accessToken: "...",
+ disableVersionCheck: true
});
```

### Q. Do I need polyfills?

Masto.js uses `fetch` and other Web APIs which may not be supported in specific environments such as the legacy version of Node.js, but we also automatically switch to another module that provides the same functionality. For example, if we detected `fetch` API is not available, we switch to `node-fetch` module. Therefore, you don't need to be aware of polyfill / ponyfill in most cases, but you will need to install them manually in some cases.

- `Node.js < 18`: We use `node-fetch`, `abort-controller`, and `form-data` as ponyfill. You don't need to install polyfills. However, if you have installed polyfills of these APIs in global, Masto.js chose them as a priority.
- `Node.js >= 18`: We use native `fetch` API. You don't need to install polyfills.
- Browsers: **We don't include any ponyfill or polyfill** in the bundle. You need to manually install abort-controller, fetch, and form-data to support legacy browsers.

## Contribution

See [CONTRIBUTING.md](CONTRIBUTING.md)
Expand Down
Loading

0 comments on commit 95c121e

Please sign in to comment.