wispjs 2.4.0 → 3.0.1

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.
@@ -0,0 +1,130 @@
1
+ import WebSocket from "ws";
2
+ import { EventEmitter } from "node:events";
3
+ /**
4
+ * A thin adapter that exposes a Socket.IO-like event API over a plain `ws`
5
+ * WebSocket.
6
+ *
7
+ * The new backend speaks a simple JSON envelope rather than the Socket.IO /
8
+ * Engine.IO framing:
9
+ *
10
+ * ```json
11
+ * { "event": "console output", "args": ["...line..."] }
12
+ * ```
13
+ *
14
+ * `args` is omitted entirely when an event carries no payload
15
+ * (e.g. `{ "event": "auth success" }`).
16
+ *
17
+ * This class translates between that envelope and the `.on/.once/.off/.emit`
18
+ * surface the rest of the codebase already uses, so the calling code barely
19
+ * changes.
20
+ *
21
+ * @remarks
22
+ * We *compose* an EventEmitter instead of extending one on purpose: extending
23
+ * it would mean overriding `emit()`, which EventEmitter also calls internally
24
+ * (e.g. the `newListener` event) — so every `.on()` would try to send a frame
25
+ * to the server. Composition keeps "emit to the wire" and "fire local
26
+ * listeners" cleanly separate.
27
+ *
28
+ * @internal
29
+ */
30
+ export class WispWebSocket {
31
+ constructor(url, opts = {}) {
32
+ this.emitter = new EventEmitter();
33
+ this.url = url;
34
+ this.origin = opts.origin;
35
+ // The pool registers a handful of listeners per worker; silence the
36
+ // default 10-listener warning.
37
+ this.emitter.setMaxListeners(0);
38
+ }
39
+ /**
40
+ * Opens the underlying WebSocket and wires up frame translation.
41
+ */
42
+ connect() {
43
+ const ws = new WebSocket(this.url, this.origin ? { origin: this.origin } : undefined);
44
+ this.ws = ws;
45
+ // Map ws lifecycle onto the Socket.IO-style events the pool listens for
46
+ ws.on("open", () => this.emitter.emit("connect"));
47
+ ws.on("close", (code, reason) => {
48
+ this.emitter.emit("disconnect", reason?.toString() || `code ${code}`);
49
+ });
50
+ ws.on("error", (err) => this.emitter.emit("connect_error", err));
51
+ ws.on("message", (data, isBinary) => {
52
+ if (isBinary)
53
+ return;
54
+ let frame;
55
+ try {
56
+ frame = JSON.parse(data.toString());
57
+ }
58
+ catch {
59
+ return; // ignore anything that isn't our JSON envelope
60
+ }
61
+ const event = frame.event;
62
+ if (!event)
63
+ return;
64
+ const args = frame.args ?? [];
65
+ // EventEmitter throws if "error" is emitted with no listener; guard it.
66
+ if (event === "error" && this.emitter.listenerCount("error") === 0) {
67
+ console.error("[ws] server error frame:", ...args);
68
+ return;
69
+ }
70
+ this.emitter.emit(event, ...args);
71
+ });
72
+ return this;
73
+ }
74
+ /**
75
+ * Sends an event to the server as a `{ event, args }` JSON frame.
76
+ *
77
+ * @remarks
78
+ * Unlike EventEmitter.emit, this does NOT fire local listeners — it writes
79
+ * to the socket. Incoming frames fire local listeners via the internal
80
+ * emitter.
81
+ */
82
+ emit(event, ...args) {
83
+ this.outgoingListener?.(event, ...args);
84
+ const frame = JSON.stringify(args.length ? { event, args } : { event });
85
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
86
+ this.ws.send(frame);
87
+ }
88
+ else {
89
+ console.error(`[ws] dropped outgoing "${event}" — socket not open`);
90
+ }
91
+ return true;
92
+ }
93
+ on(event, listener) {
94
+ this.emitter.on(event, listener);
95
+ return this;
96
+ }
97
+ once(event, listener) {
98
+ this.emitter.once(event, listener);
99
+ return this;
100
+ }
101
+ /**
102
+ * Removes a specific listener, or — matching Socket.IO's behaviour — all
103
+ * listeners for an event when no listener is given.
104
+ */
105
+ off(event, listener) {
106
+ if (listener) {
107
+ this.emitter.off(event, listener);
108
+ }
109
+ else {
110
+ this.emitter.removeAllListeners(event);
111
+ }
112
+ return this;
113
+ }
114
+ removeAllListeners(event) {
115
+ this.emitter.removeAllListeners(event);
116
+ return this;
117
+ }
118
+ /**
119
+ * Registers a callback invoked for every outgoing emit (parity with
120
+ * Socket.IO's `onAnyOutgoing`, used for logging).
121
+ */
122
+ onAnyOutgoing(listener) {
123
+ this.outgoingListener = listener;
124
+ return this;
125
+ }
126
+ disconnect() {
127
+ this.ws?.close();
128
+ return this;
129
+ }
130
+ }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "wispjs",
3
- "version": "2.4.0",
3
+ "version": "3.0.1",
4
4
  "type": "module",
5
5
  "description": "A package for interacting with Wisp-based server panels",
6
6
  "main": "dist/wisp.js",
7
7
  "types": "dist/wisp.d.ts",
8
8
  "scripts": {
9
9
  "build": "tsc",
10
- "build-docs": "typedoc wisp.ts wisp_api/index.ts wisp_socket/index.ts wisp_api/apis/*"
10
+ "build-docs": "typedoc wisp.ts wisp_api/index.ts wisp_socket/index.ts wisp_socket/git.ts wisp_socket/filesystem.ts wisp_api/apis/*"
11
11
  },
12
12
  "repository": {
13
13
  "type": "git",
@@ -20,11 +20,12 @@
20
20
  },
21
21
  "homepage": "https://github.com/CFC-Servers/WispJS#readme",
22
22
  "dependencies": {
23
- "socket.io-client": "^4.7.2",
24
- "strip-ansi": "^7.1.0"
23
+ "strip-ansi": "^7.1.0",
24
+ "ws": "^8.21.0"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^20.5.4",
28
+ "@types/ws": "^8.18.1",
28
29
  "typedoc": "^0.25.4",
29
30
  "typescript": "^5.3.3"
30
31
  }
@@ -1,77 +0,0 @@
1
- name: Deploy
2
-
3
- on:
4
- workflow_dispatch:
5
- inputs:
6
- version_tag:
7
- description: "Version tag for the package"
8
- required: true
9
-
10
- jobs:
11
- build:
12
- runs-on: ubuntu-latest
13
- steps:
14
- - uses: actions/checkout@v4
15
-
16
- - uses: actions/setup-node@v4
17
- with:
18
- node-version: 18
19
-
20
- - name: Update package version
21
- run: |
22
- version=${{ inputs.version_tag }}
23
- npm version $version --no-git-tag-version
24
-
25
- - name: Install packages
26
- run: |
27
- npm ci
28
-
29
- - name: Build
30
- run: |
31
- npm run build
32
-
33
- - name: Generate Docs
34
- run: |
35
- npm run build-docs
36
- mv docs ../docs_wip
37
-
38
- - name: Remove Non-distributables
39
- run: |
40
- rm .gitignore
41
- rm tsconfig.json
42
- rm wisp.ts
43
- rm -rf wisp_api wisp_socket .github
44
-
45
- - name: Publish
46
- uses: JS-DevTools/npm-publish@v3
47
- with:
48
- token: ${{ secrets.NPM_PUBLISH_TOKEN }}
49
-
50
- - name: Configure Git User
51
- run: |
52
- git config user.name github-actions
53
- git config user.email github-actions@github.com
54
-
55
- - name: Push package version change
56
- run: |
57
- git add package.json
58
- git add package-lock.json
59
- git commit -m "Update package.json version to: ${{ inputs.version_tag }}" && \
60
- git push --force-with-lease origin main || \
61
- echo "Version tag unchanaged"
62
-
63
- - name: Push new tag
64
- run: |
65
- git tag "${{ inputs.version_tag }}"
66
- git push --tags
67
-
68
- - name: Publish Docs
69
- run: |
70
- # Delete everything except .git
71
- find . -mindepth 1 -maxdepth 1 ! -name '.git' -exec rm -rf {} +
72
-
73
- # Move the docs back and commit only them
74
- mv ../docs_wip docs
75
- git add .
76
- git commit -m "Update Docs for version: ${{ inputs.version_tag }}"
77
- git push --force origin HEAD:docs
package/tsconfig.json DELETED
@@ -1,19 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "ESNext",
5
-
6
- "outDir": "./dist/",
7
- "declaration": true,
8
-
9
- "esModuleInterop": true,
10
- "forceConsistentCasingInFileNames": true,
11
-
12
- "strict": true,
13
- "skipLibCheck": true,
14
- "moduleResolution": "node"
15
- },
16
- "include": ["*.ts"],
17
- "exclude": ["node_modules"]
18
- }
19
-
package/wisp.ts DELETED
@@ -1,43 +0,0 @@
1
- import { WispAPI } from "./wisp_api/index.js";
2
- import { WispSocket } from "./wisp_socket/index.js";
3
-
4
- export interface WispInterface {
5
- socket: WispSocket;
6
- api: WispAPI;
7
- logger: any;
8
- }
9
-
10
- /**
11
- * The primary Wisp Interface, exposing interactions with both the HTTP and Websockets API
12
- *
13
- * @param domain The Domain of the Pterodactyl/Wisp panel (e.g. `my.gamepanel.gg`)
14
- * @param uuid The UUID of the server to reference in all API requests
15
- * @param token The panel API token to use for authorization
16
- * @param ghPAT The Github Personal Access Token used for Cloning/Pulling of private repositories. This may be omitted if you do not need to interact with private repositories
17
- *
18
- * @public
19
- */
20
- export class WispInterface {
21
- constructor(domain: string, uuid: string, token: string, ghPAT?: string) {
22
- this.logger = {
23
- info: (msg: any) => {
24
- console.log(msg);
25
- },
26
- error: (msg: string) => {
27
- console.error(msg);
28
- }
29
- };
30
-
31
- this.api = new WispAPI(domain, uuid, token, this.logger);
32
- this.socket = new WispSocket(this.logger, this.api, ghPAT);
33
- }
34
-
35
- /**
36
- * Manually disconnects from the Websocket connection(s)
37
- *
38
- * @public
39
- */
40
- async disconnect() {
41
- await this.socket.disconnect();
42
- }
43
- }
@@ -1,71 +0,0 @@
1
- import { WispAPICore } from "./index";
2
- import type { PaginationData } from "./index";
3
-
4
- /**
5
- * An Allocation object
6
- *
7
- * @internal
8
- */
9
- export interface Allocation {
10
- object: "allocation";
11
- attributes: {
12
- id: number;
13
- /** Whether or not this Allocation is the primary one for the Server */
14
- primary: boolean;
15
- ip: string;
16
- port: number;
17
- }
18
- }
19
-
20
- /**
21
- * The response object from the GetAllocations call
22
- *
23
- * @remarks
24
- * Used in {@link AllocationsAPI.List}
25
- *
26
- * @public
27
- */
28
- export interface GetAllocationsResponse {
29
- object: "list";
30
- data: Allocation[];
31
- meta: {
32
- pagination: PaginationData;
33
- }
34
- }
35
-
36
- /**
37
- * Handles the listing and updating of a Server's IP/Port Allocations
38
- *
39
- * @public
40
- */
41
- export class AllocationsAPI {
42
- constructor(private core: WispAPICore) {}
43
-
44
-
45
- /**
46
- * Lists all Allocations for the Server
47
- *
48
- * @public
49
- */
50
- async List(): Promise<GetAllocationsResponse> {
51
- const response = await this.core.makeRequest("GET", "allocations");
52
- const data: GetAllocationsResponse = await response.json()
53
-
54
- return data;
55
- }
56
-
57
-
58
- /**
59
- * Sets the new primary Allocation for the server
60
- *
61
- * @param id Allocation ID of the new primary allocation
62
- *
63
- * @public
64
- */
65
- async Update(id: string): Promise<Allocation> {
66
- const response = await this.core.makeRequest("PUT", `allocations/${id}`);
67
- const data: Allocation = await response.json()
68
-
69
- return data;
70
- }
71
- }
@@ -1,81 +0,0 @@
1
- import { WispAPICore } from "./index";
2
- import type { PaginationData } from "./index";
3
-
4
- /**
5
- * Device information
6
- *
7
- * @example
8
- * ```json
9
- * {
10
- * "city_name": "Seattle",
11
- * "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0",
12
- * "country_name": "US",
13
- * "country_iso_code": "US"
14
- * }
15
- * ```
16
- *
17
- * @internal
18
- */
19
- export interface Device {
20
- city_name: string;
21
- user_agent: string;
22
- country_name: string;
23
- country_iso_code: string;
24
- }
25
-
26
- // TODO: Fully define the audit log type
27
- /**
28
- * An Audit Log struct
29
- *
30
- * @internal
31
- */
32
- export interface AuditLog {
33
- object: "audit_log";
34
- attributes: {
35
- action: string;
36
- subaction: string;
37
- device: Device | undefined;
38
- metadata: any;
39
- created_at: string;
40
- }
41
- }
42
-
43
- /**
44
- * The respones object from the GetAuditLogs call
45
- *
46
- * @remarks
47
- * Used in {@link AuditLogsAPI.List}
48
- *
49
- * @public
50
- */
51
- export interface GetAuditLogsResponse {
52
- object: "list";
53
- data: AuditLog[];
54
- meta: {
55
- pagination: PaginationData;
56
- }
57
- }
58
-
59
-
60
- /**
61
- * Interface that handles Listing of all Audit Logs
62
- *
63
- * @public
64
- */
65
- export class AuditLogsAPI {
66
- constructor(private core: WispAPICore) {}
67
-
68
-
69
- // TODO: Handle pagination
70
- /**
71
- * List all Audit Log events for the server
72
- *
73
- * @public
74
- */
75
- async List(): Promise<GetAuditLogsResponse> {
76
- const response = await this.core.makeRequest("GET", "audit-logs");
77
- const data: GetAuditLogsResponse = await response.json();
78
-
79
- return data;
80
- }
81
- }
@@ -1,168 +0,0 @@
1
- import { WispAPICore } from "./index";
2
- import type { PaginationData } from "./index";
3
- import type { DownloadFileResponse } from "./filesystem";
4
-
5
- /**
6
- * A Backup Object
7
- * @example
8
- * ```json
9
- * {
10
- * "object": "backup",
11
- * "attributes": {
12
- * "uuid": "26adeafc-74af-43fd-93a5-9afa0486b21b",
13
- * "uuid_short": "26adeafc",
14
- * "name": "test-1",
15
- * "sha256_hash": "56d2965e9167c785647878a391dfe24460c3aa5e1111b385708094bd8837b487",
16
- * "bytes": 3266448936,
17
- * "locked": false,
18
- * "creating": false,
19
- * "created_at": "2023-11-28T08:37:39.000000Z"
20
- * }
21
- * }
22
- * ```
23
- *
24
- * @internal
25
- */
26
- export interface Backup {
27
- object: "backup";
28
- attributes: {
29
- uuid: string;
30
- uuid_short: string;
31
- name: string;
32
- /** The hash of the Backup. May be null if the Backup is still being created. */
33
- sha256_hash: string | null;
34
- bytes: number;
35
- locked: boolean;
36
- creating: boolean;
37
- created_at: string;
38
- }
39
- }
40
-
41
- /**
42
- * Response object used in the GetBackups call
43
- *
44
- * @remarks
45
- * Used in {@link BackupsAPI.List}
46
- *
47
- * @internal
48
- */
49
- export interface GetBackupsResponse {
50
- object: "list";
51
- data: Backup[];
52
- meta: {
53
- pagination: PaginationData;
54
- }
55
- }
56
-
57
- export type BackupErrorCode = "server.backups.creation_would_exceed_limit";
58
- export interface BackupError {
59
- code: BackupErrorCode;
60
- data: any;
61
- }
62
- export interface CreateBackupFailure {
63
- errors: BackupError[] | undefined
64
- }
65
- export type CreateBackupResponse = Backup | CreateBackupFailure;
66
-
67
- /**
68
- * Handles basic server backup tasks, such as creating, restoring, and deleting backups
69
- */
70
- export class BackupsAPI {
71
- constructor(private core: WispAPICore) {}
72
-
73
- /**
74
- * Lists all current backups for the server
75
- *
76
- * @public
77
- */
78
- async List(): Promise<GetBackupsResponse> {
79
- const response = await this.core.makeRequest("GET", "backups");
80
- const data: GetBackupsResponse = await response.json();
81
-
82
- return data;
83
- }
84
-
85
- /**
86
- * Creates a new backup for the server
87
- *
88
- * @remarks
89
- * ⚠️ This can fail to create a Backup even if the function completes successfully
90
- * For example, if the backup would exceed the size limit (and the limit is not 0), the system wouldn't know it failed until it hit the limit.
91
- *
92
- * ⚠️ "It is recomended to stop your server before starting a backup. Backups created while the server is on can contain corupted data."
93
- *
94
- * Multiple Backups can exist with the same name.
95
- *
96
- * @param name The name of the Backup
97
- *
98
- * @throws {@link BackupErrorCode}
99
- * If the server returns an error code, it will be thrown verbatim here
100
- *
101
- * @public
102
- */
103
- async Create(name: string): Promise<CreateBackupResponse> {
104
- const response = await this.core.makeRequest("POST", "backups", { name: name });
105
- const data: CreateBackupResponse = await response.json()
106
-
107
- if ("errors" in data && data.errors) {
108
- throw new Error(data.errors[0].code);
109
- }
110
-
111
- return data
112
- }
113
-
114
- /**
115
- * Toggles the "Locked" status of the Backup
116
- *
117
- * @param id The ID of the Backup
118
- *
119
- * @public
120
- */
121
- async ToggleLock(id: string): Promise<Backup> {
122
- const response = await this.core.makeRequest("POST", `backups/${id}/locked`);
123
- const data: Backup = await response.json();
124
-
125
- return data;
126
- }
127
-
128
- /**
129
- * Deploys the Backup to the Server
130
- *
131
- * @remarks
132
- * **⚠️ This can be dangerous!**
133
- * The Backup will overwrite the entire Server, erasing any new data since the Backup's creation
134
- *
135
- * @param id The ID of the Backup
136
- *
137
- * @public
138
- */
139
- async Deploy(id: string): Promise<Response> {
140
- return await this.core.makeRequest("POST", `backups/${id}/deploy`);
141
- }
142
-
143
- /**
144
- * Retrieves a URL from which the Backup can be downloaded
145
- *
146
- * @param id The ID of the Backup
147
- * @returns The download URL
148
- *
149
- * @public
150
- */
151
- async GetDownloadURL(id: string): Promise<string> {
152
- const response = await this.core.makeRequest("GET", `backups/${id}/download`);
153
- const data: DownloadFileResponse = await response.json();
154
-
155
- return data.url;
156
- }
157
-
158
- /**
159
- * Deletes the Backup
160
- *
161
- * @param id The ID of the Backup
162
- *
163
- * @public
164
- */
165
- async Delete(id: string): Promise<void> {
166
- await this.core.makeRequest("DELETE", `backups/${id}`);
167
- }
168
- }
@@ -1,80 +0,0 @@
1
- import { WispAPICore } from "./index";
2
- import type { PaginationData } from "./index";
3
-
4
- export interface DatabaseRelationship {
5
- object: "database_host";
6
- attributes: {
7
- id: number;
8
- name: string;
9
- host: string;
10
- port: number;
11
- phpmyadmin_url: string | null;
12
- }
13
- }
14
-
15
- export interface Database {
16
- object: "database";
17
- attributes: {
18
- id: number;
19
- name: string;
20
- remote: string;
21
- username: string;
22
- password: string;
23
- relationships: DatabaseRelationship[];
24
- }
25
- }
26
- export interface GetDatabasesResponse {
27
- object: "list";
28
- data: Database[];
29
- meta: {
30
- pagination: PaginationData;
31
- }
32
- }
33
-
34
- /**
35
- * Handles Creating, Listing, Updating, and Deleting of Databases for the Server
36
- *
37
- * @public
38
- */
39
- export class DatabasesAPI {
40
- constructor(private core: WispAPICore) {}
41
-
42
- // TODO: Handle Pagination
43
- /**
44
- * Lists all Databases associated with the Server
45
- *
46
- * @public
47
- */
48
- async List(): Promise<GetDatabasesResponse> {
49
- const response = await this.core.makeRequest("GET", "databases", { include: "hosts" });
50
- const data: GetDatabasesResponse = await response.json();
51
-
52
- return data;
53
- }
54
-
55
-
56
- // TODO: verify response
57
- /**
58
- * Deletes the Database from the Server
59
- *
60
- * @param id The ID of the Backup
61
- *
62
- * @public
63
- */
64
- async Delete(id: string): Promise<Response> {
65
- return await this.core.makeRequest("DELETE", `databases/${id}`);
66
- }
67
-
68
-
69
- // TODO: Verify response
70
- /**
71
- * Rotates the password for the Backup
72
- *
73
- * @param id The ID of the Backup
74
- *
75
- * @public
76
- */
77
- async RotatePassword(id: string): Promise<Response> {
78
- return await this.core.makeRequest("POST", `databases/${id}`);
79
- }
80
- }