@forge-dashboard/sdk-node 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ryan Kes
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# forge-dashboard-sdk-node
|
|
2
|
+
|
|
3
|
+
[](https://github.com/alrayyes/forge-dashboard-sdk-node/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@forge-dashboard/sdk-node)
|
|
5
|
+
[](https://codecov.io/gh/alrayyes/forge-dashboard-sdk-node)
|
|
6
|
+
[](https://alrayyes.github.io/forge-dashboard-sdk-node/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
A Node/TypeScript client for [forge-dashboard](https://github.com/alrayyes/forge-dashboard)'s
|
|
10
|
+
REST API, generated from its OpenAPI spec with
|
|
11
|
+
[openapi-typescript](https://openapi-ts.dev/) and
|
|
12
|
+
[openapi-fetch](https://openapi-ts.dev/openapi-fetch/). It saves you from
|
|
13
|
+
hand-rolling HTTP requests and retries against the API yourself.
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
|
|
17
|
+
- Node.js 20+ or [Bun](https://bun.sh) 1.1+ — anything with a native `fetch`.
|
|
18
|
+
- A running forge-dashboard instance.
|
|
19
|
+
- A personal API token for that instance (see "Authentication" below) —
|
|
20
|
+
every endpoint except `/healthz` and `/api/version` needs one.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
bun add @forge-dashboard/sdk-node
|
|
26
|
+
# or: npm install @forge-dashboard/sdk-node
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Alternative registry: GitHub Packages
|
|
30
|
+
|
|
31
|
+
Every release also publishes to GitHub Packages under
|
|
32
|
+
`@alrayyes/forge-dashboard-sdk-node` — the same code, a different scope,
|
|
33
|
+
because GitHub Packages' npm registry requires a package scoped to the
|
|
34
|
+
repo owner rather than the `forge-dashboard` npm org. Worth it if you're
|
|
35
|
+
already authenticated to GitHub (CI in another of your own repos, say) and
|
|
36
|
+
would rather not hold a separate npmjs.com credential just to install this
|
|
37
|
+
one package.
|
|
38
|
+
|
|
39
|
+
Add a `.npmrc` pointing that scope at GitHub Packages:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
@alrayyes:registry=https://npm.pkg.github.com
|
|
43
|
+
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
then install the scoped name instead:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
npm install @alrayyes/forge-dashboard-sdk-node
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Authentication
|
|
53
|
+
|
|
54
|
+
forge-dashboard authenticates browsers with passkeys (WebAuthn) and a
|
|
55
|
+
session cookie, but its spec documents a real headless alternative: a
|
|
56
|
+
personal API token, generated from the dashboard's own Settings page
|
|
57
|
+
(`POST /api/tokens`, signed in as yourself first) and sent as
|
|
58
|
+
`Authorization: Bearer <token>` on every request after. Pass it to
|
|
59
|
+
`createForgeDashboardClient` or set `FORGE_DASHBOARD_TOKEN` in the
|
|
60
|
+
environment:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { createForgeDashboardClient } from "@forge-dashboard/sdk-node";
|
|
64
|
+
|
|
65
|
+
const client = createForgeDashboardClient("https://dashboard.example.com", {
|
|
66
|
+
token: process.env.FORGE_DASHBOARD_TOKEN,
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
A token is revocable from Settings at any point; there's nothing in this
|
|
71
|
+
SDK to refresh one automatically once it's gone.
|
|
72
|
+
|
|
73
|
+
## Usage
|
|
74
|
+
|
|
75
|
+
`GET /api/version` needs no token and is a good first call to prove the
|
|
76
|
+
client reaches the server at all. The client is a typed
|
|
77
|
+
[`openapi-fetch`](https://openapi-ts.dev/openapi-fetch/) instance, so every
|
|
78
|
+
operation is `client.GET(path, ...)` / `client.POST(path, ...)`, returning
|
|
79
|
+
`{ data, error, response }`:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { createForgeDashboardClient } from "@forge-dashboard/sdk-node";
|
|
83
|
+
|
|
84
|
+
const client = createForgeDashboardClient("https://dashboard.example.com");
|
|
85
|
+
|
|
86
|
+
const { data } = await client.GET("/api/version", {});
|
|
87
|
+
console.log("server version:", data?.version);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Fetching your own dashboard needs a token, and demonstrates typed error
|
|
91
|
+
handling:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import {
|
|
95
|
+
ApiError,
|
|
96
|
+
createForgeDashboardClient,
|
|
97
|
+
decodeError,
|
|
98
|
+
} from "@forge-dashboard/sdk-node";
|
|
99
|
+
|
|
100
|
+
const client = createForgeDashboardClient("https://dashboard.example.com", {
|
|
101
|
+
token: process.env.FORGE_DASHBOARD_TOKEN,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const { data, error, response } = await client.GET("/api/dashboard", {});
|
|
105
|
+
if (error) {
|
|
106
|
+
const apiErr = decodeError(response, error);
|
|
107
|
+
if (apiErr instanceof ApiError && apiErr.statusCode === 401) {
|
|
108
|
+
throw new Error("token expired or invalid");
|
|
109
|
+
}
|
|
110
|
+
throw apiErr;
|
|
111
|
+
}
|
|
112
|
+
for (const pr of data.pullRequests) {
|
|
113
|
+
console.log(`${pr.repo}#${pr.number}: ${pr.title} (${pr.ci})`);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Every other operation follows the same `client.GET`/`client.POST`/... pattern.
|
|
118
|
+
Use `decodeError` to turn any failed response into a
|
|
119
|
+
`@forge-dashboard/sdk-node` `ApiError` uniformly, as shown above.
|
|
120
|
+
|
|
121
|
+
The client retries a `429` or `5xx` response with exponential backoff and
|
|
122
|
+
jitter (honoring a server-sent `Retry-After`), and never retries any other
|
|
123
|
+
`4xx`. Tune it with the `retry` option, or swap the underlying `fetch`
|
|
124
|
+
entirely with the `fetch` option.
|
|
125
|
+
|
|
126
|
+
## Regenerating the types
|
|
127
|
+
|
|
128
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) — the generated types are pinned to a
|
|
129
|
+
specific forge-dashboard commit and shouldn't drift from it silently.
|
|
130
|
+
|
|
131
|
+
## Contributing
|
|
132
|
+
|
|
133
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for building, testing and the release
|
|
134
|
+
process.
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
[MIT](LICENSE) — a permissive license for the client, independent of
|
|
139
|
+
forge-dashboard's own AGPL-3.0.
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@forge-dashboard/sdk-node",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Node/TypeScript client for forge-dashboard's REST API, generated from its OpenAPI spec.",
|
|
5
|
+
"homepage": "https://github.com/alrayyes/forge-dashboard-sdk-node",
|
|
6
|
+
"bugs": "https://github.com/alrayyes/forge-dashboard-sdk-node/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/alrayyes/forge-dashboard-sdk-node.git"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc -p tsconfig.build.json",
|
|
26
|
+
"docs": "typedoc",
|
|
27
|
+
"generate": "openapi-typescript openapi/openapi.yaml -o src/generated/schema.ts",
|
|
28
|
+
"lint": "biome check .",
|
|
29
|
+
"lint:fix": "biome check --write .",
|
|
30
|
+
"test": "bun test",
|
|
31
|
+
"test:ci": "bun test --coverage --coverage-reporter=lcov --reporter=junit --reporter-outfile=junit.xml",
|
|
32
|
+
"test:mutation": "stryker run",
|
|
33
|
+
"typecheck": "tsc --noEmit"
|
|
34
|
+
},
|
|
35
|
+
"overrides": {
|
|
36
|
+
"qs": "6.16.0",
|
|
37
|
+
"smol-toml": "1.8.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"openapi-fetch": "0.17.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@biomejs/biome": "2.5.14",
|
|
44
|
+
"@commitlint/cli": "21.2.2",
|
|
45
|
+
"@commitlint/config-conventional": "21.2.2",
|
|
46
|
+
"@hughescr/stryker-bun-runner": "1.4.0",
|
|
47
|
+
"@stryker-mutator/core": "10.0.0",
|
|
48
|
+
"@stryker-mutator/typescript-checker": "10.0.0",
|
|
49
|
+
"@types/bun": "1.4.2",
|
|
50
|
+
"@types/node": "26.6.1",
|
|
51
|
+
"openapi-typescript": "7.13.0",
|
|
52
|
+
"sort-package-json": "4.0.0",
|
|
53
|
+
"typedoc": "0.28.20",
|
|
54
|
+
"typescript": "6.0.3"
|
|
55
|
+
},
|
|
56
|
+
"packageManager": "bun@1.3.14",
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=20"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
}
|
|
63
|
+
}
|