erlc-api 2.3.4 → 3.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.
@@ -0,0 +1,53 @@
1
+ const { BASEURL } = require("../../constants.js");
2
+ const { processError } = require("../../utils/errorHandler.js");
3
+
4
+ /**
5
+ * Retrieves server staff information
6
+ * @param {string} serverToken - The server API key
7
+ * @returns {Promise<Object>} Promise that resolves to server staff object
8
+ */
9
+ module.exports = (serverToken) => {
10
+ return new Promise(async (resolve, reject) => {
11
+ // Input validation
12
+ if (!serverToken || typeof serverToken !== "string") {
13
+ return reject(new Error("Server token is required and must be a string"));
14
+ }
15
+
16
+ try {
17
+ const fetch = await import("node-fetch");
18
+ const { config } = await import("../../erlc.js");
19
+
20
+ // Check if global token is configured
21
+ if (!config?.globalToken) {
22
+ const error = await processError(
23
+ new Error(
24
+ "Global token not configured. Please initialize the client first."
25
+ )
26
+ );
27
+ return reject(error);
28
+ }
29
+
30
+ const res = await fetch.default(`${BASEURL}/server/staff`, {
31
+ headers: {
32
+ Authorization: config.globalToken,
33
+ "Server-Key": serverToken,
34
+ },
35
+ timeout: 10000, // 10 second timeout
36
+ });
37
+
38
+ if (!res.ok) {
39
+ const errorData = await res
40
+ .json()
41
+ .catch(() => ({ error: "Unknown API error" }));
42
+ const error = await processError(res, errorData);
43
+ return reject(error);
44
+ }
45
+
46
+ const data = await res.json();
47
+ resolve(data || { CoOwners: [], Admins: {}, Mods: {} });
48
+ } catch (error) {
49
+ const processedError = await processError(error);
50
+ reject(processedError);
51
+ }
52
+ });
53
+ };
@@ -1,28 +1,53 @@
1
- const { BASEURL } = require("../../constants.js");
2
-
3
- module.exports = (serverToken) => {
4
- return new Promise(async (resolve, reject) => {
5
- try {
6
- const fetch = await import("node-fetch");
7
- const { config } = await import("../../erlc.js");
8
-
9
- const res = await fetch.default(`${BASEURL}/server/vehicles`, {
10
- headers: {
11
- Authorization: config?.globalToken,
12
- "Server-Key": serverToken,
13
- },
14
- });
15
- const data = await res.json().catch((err) => {
16
- return reject(err);
17
- });
18
-
19
- if (!res.ok) {
20
- return reject(data);
21
- }
22
-
23
- resolve(data);
24
- } catch (error) {
25
- reject(error);
26
- }
27
- });
28
- };
1
+ const { BASEURL } = require("../../constants.js");
2
+ const { processError } = require("../../utils/errorHandler.js");
3
+
4
+ /**
5
+ * Retrieves server vehicles information
6
+ * @param {string} serverToken - The server API key
7
+ * @returns {Promise<Array>} Promise that resolves to array of vehicles
8
+ */
9
+ module.exports = (serverToken) => {
10
+ return new Promise(async (resolve, reject) => {
11
+ // Input validation
12
+ if (!serverToken || typeof serverToken !== "string") {
13
+ return reject(new Error("Server token is required and must be a string"));
14
+ }
15
+
16
+ try {
17
+ const fetch = await import("node-fetch");
18
+ const { config } = await import("../../erlc.js");
19
+
20
+ // Check if global token is configured
21
+ if (!config?.globalToken) {
22
+ const error = await processError(
23
+ new Error(
24
+ "Global token not configured. Please initialize the client first."
25
+ )
26
+ );
27
+ return reject(error);
28
+ }
29
+
30
+ const res = await fetch.default(`${BASEURL}/server/vehicles`, {
31
+ headers: {
32
+ Authorization: config.globalToken,
33
+ "Server-Key": serverToken,
34
+ },
35
+ timeout: 10000, // 10 second timeout
36
+ });
37
+
38
+ if (!res.ok) {
39
+ const errorData = await res
40
+ .json()
41
+ .catch(() => ({ error: "Unknown API error" }));
42
+ const error = await processError(res, errorData);
43
+ return reject(error);
44
+ }
45
+
46
+ const data = await res.json();
47
+ resolve(Array.isArray(data) ? data : []);
48
+ } catch (error) {
49
+ const processedError = await processError(error);
50
+ reject(processedError);
51
+ }
52
+ });
53
+ };
@@ -1,29 +1,67 @@
1
- const { BASEURL } = require("../../constants.js");
2
-
3
- module.exports = (serverToken, command) => {
4
- return new Promise(async (resolve, reject) => {
5
- try {
6
- const fetch = await import("node-fetch");
7
- const { config } = await import("../../erlc.js");
8
- const params = JSON.stringify({ command: `${command}` });
9
- const res = await fetch.default(`${BASEURL}/server/command`, {
10
- headers: {
11
- Authorization: config?.globalToken,
12
- "Server-Key": serverToken,
13
- "Content-Type": "application/json",
14
- },
15
- method: "POST",
16
- body: params,
17
- });
18
-
19
- if (!res.ok) {
20
- const errorData = await res.json();
21
- return reject(errorData);
22
- }
23
-
24
- resolve(true);
25
- } catch (error) {
26
- reject(error);
27
- }
28
- });
29
- };
1
+ const { BASEURL } = require("../../constants.js");
2
+ const { processError } = require("../../utils/errorHandler.js");
3
+
4
+ /**
5
+ * Executes a command on the server
6
+ * @param {string} serverToken - The server API key
7
+ * @param {string} command - The command to execute
8
+ * @returns {Promise<boolean>} Promise that resolves to true if command was executed successfully
9
+ */
10
+ module.exports = (serverToken, command) => {
11
+ return new Promise(async (resolve, reject) => {
12
+ // Input validation
13
+ if (!serverToken || typeof serverToken !== "string") {
14
+ return reject(new Error("Server token is required and must be a string"));
15
+ }
16
+
17
+ if (!command || typeof command !== "string") {
18
+ return reject(new Error("Command is required and must be a string"));
19
+ }
20
+
21
+ if (command.trim().length === 0) {
22
+ return reject(new Error("Command cannot be empty"));
23
+ }
24
+
25
+ try {
26
+ const fetch = await import("node-fetch");
27
+ const { config } = await import("../../erlc.js");
28
+
29
+ // Check if global token is configured
30
+ if (!config?.globalToken) {
31
+ const error = await processError(
32
+ new Error(
33
+ "Global token not configured. Please initialize the client first."
34
+ )
35
+ );
36
+ return reject(error);
37
+ }
38
+
39
+ const requestBody = JSON.stringify({ command: command.trim() });
40
+
41
+ const res = await fetch.default(`${BASEURL}/server/command`, {
42
+ method: "POST",
43
+ headers: {
44
+ Authorization: config.globalToken,
45
+ "Server-Key": serverToken,
46
+ "Content-Type": "application/json",
47
+ },
48
+ body: requestBody,
49
+ timeout: 15000, // 15 second timeout for commands
50
+ });
51
+
52
+ if (!res.ok) {
53
+ const errorData = await res
54
+ .json()
55
+ .catch(() => ({ error: "Unknown API error" }));
56
+ const error = await processError(res, errorData);
57
+ return reject(error);
58
+ }
59
+
60
+ // Command executed successfully
61
+ resolve(true);
62
+ } catch (error) {
63
+ const processedError = await processError(error);
64
+ reject(processedError);
65
+ }
66
+ });
67
+ };
@@ -1,5 +1,5 @@
1
- declare module 'NodeModule' {
2
- interface NodeModule {
3
- type?: string;
4
- }
1
+ declare module 'NodeModule' {
2
+ interface NodeModule {
3
+ type?: string;
4
+ }
5
5
  }
@@ -1,91 +1,127 @@
1
- declare module "erlc-api" {
2
- export interface ClientConfig {
3
- globalToken: string; // The ER:LC global API token
4
- }
5
-
6
- export const BASEURL = "https://api.policeroleplay.community/v1";
7
-
8
- type PlayerId = string;
9
- type PlayerName = string;
10
- type TextureName = string;
11
- type CarName = string;
12
-
13
- export type ErlcPlayer = `${PlayerName}:${PlayerId}`; // Playername:UserID
14
- export type ErlcPlayerPermission =
15
- | "Normal"
16
- | "Server Administrator"
17
- | "Server Owner"
18
- | "Server Moderator";
19
-
20
- export interface ServerStatus {
21
- Name: string; // The server name
22
- OwnerId: number; // The roblox id of the server owner
23
- CoOwnerIds: number[]; // The roblox ids of the server co owners
24
- CurrentPlayers: number; // The amount of people currently in-game
25
- MaxPlayers: number; // The amount of people who can join the server including server owner
26
- JoinKey: string; // The code used to join the private server
27
- AccVerifiedReq: "Disabled" | "Email" | "Phone/ID"; // The level of verification roblox accounts need to join the private server
28
- TeamBalance: boolean; // If team balance is enabled or not
29
- }
30
-
31
- export interface ServerPlayer {
32
- Player: ErlcPlayer;
33
- Permission: ErlcPlayerPermission;
34
- }
35
-
36
- export interface JoinLog {
37
- Join: boolean; // True is join and False is leave
38
- Timestamp: number; // Timestamp in seconds
39
- Player: ErlcPlayer;
40
- }
41
-
42
- export interface KillLog {
43
- Killed: ErlcPlayer;
44
- Timestamp: number; // Timestamp in seconds
45
- Killer: ErlcPlayer;
46
- }
47
-
48
- export interface CommandLog {
49
- Player: ErlcPlayer;
50
- Timestamp: number; // Timestamp in seconds
51
- Command: string;
52
- }
53
-
54
- export interface ModcallLog {
55
- Caller: ErlcPlayer;
56
- Moderator?: ErlcPlayer; // If call is unanswered property is undefined
57
- Timestamp: number; // Timestamp in seconds
58
- }
59
-
60
- export type ServerBan = Record<PlayerId, PlayerName>;
61
-
62
- export interface VehiclesLog {
63
- Texture: string | null;
64
- Name: string;
65
- Owner: ErlcPlayer;
66
- }
67
-
68
- export interface VSMCommandBody {
69
- command: string; // ":h Hey everyone!"
70
- }
71
-
72
-
73
- export function getBans(serverToken: string): Promise<ServerBan>;
74
- export function getCommandLogs(serverToken: string): Promise<CommandLog[]>;
75
- export function getJoinLogs(serverToken: string): Promise<JoinLog[]>;
76
- export function getKillLogs(serverToken: string): Promise<KillLog[]>;
77
- export function getModcallLogs(serverToken: string): Promise<ModcallLog[]>;
78
- export function getPlayers(serverToken: string): Promise<ServerPlayer[]>;
79
- export function getQueue(serverToken: string): Promise<number[]>;
80
- export function getServer(serverToken: string): Promise<ServerStatus>;
81
- export function getVehicles(serverToken: string): Promise<VehiclesLog[]>;
82
- export function runCommand(
83
- serverToken: string,
84
- command: string
85
- ): Promise<boolean>;
86
-
87
- export class Client {
88
- constructor(options: ClientConfig);
89
- config(): ClientConfig;
90
- }
91
- }
1
+ declare module "erlc-api" {
2
+ // Error handling types
3
+ export class ErlcError extends Error {
4
+ code: string | number;
5
+ status?: number;
6
+ category?: string;
7
+ severity?: string;
8
+ suggestions?: string[];
9
+ retryable?: boolean;
10
+ timestamp: string;
11
+ originalError?: Error;
12
+
13
+ constructor(
14
+ message: string,
15
+ code: string | number,
16
+ status?: number,
17
+ originalError?: Error
18
+ );
19
+ toJSON(): object;
20
+ toString(): string;
21
+ }
22
+
23
+ export interface ErrorInfo {
24
+ message: string;
25
+ description: string;
26
+ category: string;
27
+ severity: string;
28
+ code?: number;
29
+ }
30
+
31
+ export interface ClientConfig {
32
+ globalToken: string; // The ER:LC global API token
33
+ }
34
+
35
+ export const BASEURL = "https://api.policeroleplay.community/v1";
36
+
37
+ type PlayerId = string;
38
+ type PlayerName = string;
39
+ type TextureName = string;
40
+ type CarName = string;
41
+
42
+ export type ErlcPlayer = `${PlayerName}:${PlayerId}`; // Playername:UserID
43
+ export type ErlcPlayerPermission =
44
+ | "Normal"
45
+ | "Server Administrator"
46
+ | "Server Owner"
47
+ | "Server Moderator";
48
+
49
+ export interface ServerStatus {
50
+ Name: string; // The server name
51
+ OwnerUsername: string; // The username of the server owner
52
+ CoOwnerUsernames: string[]; // The usernames of the server co owners
53
+ CurrentPlayers: number; // The amount of people currently in-game
54
+ MaxPlayers: number; // The amount of people who can join the server including server owner
55
+ JoinKey: string; // The code used to join the private server
56
+ AccVerifiedReq: "Disabled" | "Email" | "Phone/ID"; // The level of verification roblox accounts need to join the private server
57
+ TeamBalance: boolean; // If team balance is enabled or not
58
+ VanityURL: string; // The vanity URL to join the server
59
+ }
60
+
61
+ export interface ServerPlayer {
62
+ Player: ErlcPlayer;
63
+ Permission: ErlcPlayerPermission;
64
+ }
65
+
66
+ export interface JoinLog {
67
+ Join: boolean; // True is join and False is leave
68
+ Timestamp: number; // Timestamp in seconds
69
+ Player: ErlcPlayer;
70
+ }
71
+
72
+ export interface KillLog {
73
+ Killed: ErlcPlayer;
74
+ Timestamp: number; // Timestamp in seconds
75
+ Killer: ErlcPlayer;
76
+ }
77
+
78
+ export interface CommandLog {
79
+ Player: ErlcPlayer;
80
+ Timestamp: number; // Timestamp in seconds
81
+ Command: string;
82
+ }
83
+
84
+ export interface ModcallLog {
85
+ Caller: ErlcPlayer;
86
+ Moderator?: ErlcPlayer; // If call is unanswered property is undefined
87
+ Timestamp: number; // Timestamp in seconds
88
+ }
89
+
90
+ export type ServerBan = Record<PlayerId, PlayerName>;
91
+
92
+ export interface VehiclesLog {
93
+ Texture: string | null;
94
+ Name: string;
95
+ Owner: ErlcPlayer;
96
+ }
97
+
98
+ export interface ServerStaff {
99
+ CoOwners: number[];
100
+ Admins: Record<string, string>;
101
+ Mods: Record<string, string>;
102
+ }
103
+
104
+ export interface VSMCommandBody {
105
+ command: string; // ":h Hey everyone!"
106
+ }
107
+
108
+ export function getBans(serverToken: string): Promise<ServerBan>;
109
+ export function getCommandLogs(serverToken: string): Promise<CommandLog[]>;
110
+ export function getJoinLogs(serverToken: string): Promise<JoinLog[]>;
111
+ export function getKillLogs(serverToken: string): Promise<KillLog[]>;
112
+ export function getModcallLogs(serverToken: string): Promise<ModcallLog[]>;
113
+ export function getPlayers(serverToken: string): Promise<ServerPlayer[]>;
114
+ export function getQueue(serverToken: string): Promise<number[]>;
115
+ export function getServer(serverToken: string): Promise<ServerStatus>;
116
+ export function getStaff(serverToken: string): Promise<ServerStaff>;
117
+ export function getVehicles(serverToken: string): Promise<VehiclesLog[]>;
118
+ export function runCommand(
119
+ serverToken: string,
120
+ command: string
121
+ ): Promise<boolean>;
122
+
123
+ export class Client {
124
+ constructor(options: ClientConfig);
125
+ config(): ClientConfig;
126
+ }
127
+ }
@@ -0,0 +1,158 @@
1
+ const ErlcError = require("../errors/ErlcError.js");
2
+ const {
3
+ getErrorInfo,
4
+ getSuggestedActions,
5
+ isRetryableError,
6
+ } = require("../errors/errorCodes.js");
7
+
8
+ /**
9
+ * Handles API response errors and creates appropriate ErlcError instances
10
+ * @param {Response} response - The fetch response object
11
+ * @param {Object} errorData - The parsed error data from response
12
+ * @returns {ErlcError} Formatted ERLC error
13
+ */
14
+ function handleApiError(response, errorData) {
15
+ const status = response.status;
16
+
17
+ // Check if error data contains ERLC error code
18
+ if (errorData && typeof errorData.code === "number") {
19
+ const errorInfo = getErrorInfo(errorData.code);
20
+ const suggestions = getSuggestedActions(errorData.code);
21
+
22
+ const message = `${errorInfo.message}: ${errorInfo.description}`;
23
+ const error = new ErlcError(message, errorData.code, status);
24
+ error.category = errorInfo.category;
25
+ error.severity = errorInfo.severity;
26
+ error.suggestions = suggestions;
27
+ error.retryable = isRetryableError(errorData.code);
28
+
29
+ return error;
30
+ }
31
+
32
+ // Handle HTTP status codes when no ERLC error code is provided
33
+ let message, code;
34
+
35
+ switch (status) {
36
+ case 400:
37
+ message = "Bad Request: The request was invalid or malformed";
38
+ code = "HTTP_400";
39
+ break;
40
+ case 401:
41
+ message = "Unauthorized: Authentication failed or missing credentials";
42
+ code = "HTTP_401";
43
+ break;
44
+ case 403:
45
+ message = "Forbidden: Access denied or insufficient permissions";
46
+ code = "HTTP_403";
47
+ break;
48
+ case 404:
49
+ message = "Not Found: The requested resource was not found";
50
+ code = "HTTP_404";
51
+ break;
52
+ case 422:
53
+ message = "Unprocessable Entity: The server has no players";
54
+ code = "HTTP_422";
55
+ break;
56
+ case 429:
57
+ message = "Too Many Requests: Rate limit exceeded";
58
+ code = "HTTP_429";
59
+ break;
60
+ case 500:
61
+ message = "Internal Server Error: Problem communicating with Roblox";
62
+ code = "HTTP_500";
63
+ break;
64
+ case 502:
65
+ message = "Bad Gateway: Server received invalid response";
66
+ code = "HTTP_502";
67
+ break;
68
+ case 503:
69
+ message = "Service Unavailable: Server temporarily unavailable";
70
+ code = "HTTP_503";
71
+ break;
72
+ default:
73
+ message = `HTTP Error ${status}: ${
74
+ errorData?.error || response.statusText || "Unknown error"
75
+ }`;
76
+ code = `HTTP_${status}`;
77
+ }
78
+
79
+ const error = new ErlcError(message, code, status);
80
+ error.category = "HTTP_ERROR";
81
+ error.severity = status >= 500 ? "HIGH" : "MEDIUM";
82
+ error.retryable = [429, 500, 502, 503].includes(status);
83
+
84
+ return error;
85
+ }
86
+
87
+ /**
88
+ * Handles network and other non-API errors
89
+ * @param {Error} originalError - The original error object
90
+ * @returns {ErlcError} Formatted ERLC error
91
+ */
92
+ function handleNetworkError(originalError) {
93
+ let message, code, category;
94
+
95
+ if (originalError.code === "ENOTFOUND") {
96
+ message =
97
+ "Network Error: Unable to connect to ERLC API (DNS resolution failed)";
98
+ code = "NETWORK_DNS_ERROR";
99
+ category = "NETWORK_ERROR";
100
+ } else if (originalError.code === "ECONNREFUSED") {
101
+ message = "Network Error: Connection refused by ERLC API server";
102
+ code = "NETWORK_CONNECTION_REFUSED";
103
+ category = "NETWORK_ERROR";
104
+ } else if (
105
+ originalError.code === "ETIMEDOUT" ||
106
+ originalError.name === "AbortError"
107
+ ) {
108
+ message = "Request Timeout: API took too long to respond";
109
+ code = "REQUEST_TIMEOUT";
110
+ category = "TIMEOUT_ERROR";
111
+ } else if (originalError.message?.includes("JSON")) {
112
+ message = "Parse Error: Invalid JSON response from API";
113
+ code = "JSON_PARSE_ERROR";
114
+ category = "PARSE_ERROR";
115
+ } else {
116
+ message = `Unexpected Error: ${originalError.message}`;
117
+ code = "UNEXPECTED_ERROR";
118
+ category = "UNKNOWN_ERROR";
119
+ }
120
+
121
+ const error = new ErlcError(message, code, null, originalError);
122
+ error.category = category;
123
+ error.severity = "MEDIUM";
124
+ error.retryable = [
125
+ "NETWORK_DNS_ERROR",
126
+ "REQUEST_TIMEOUT",
127
+ "NETWORK_CONNECTION_REFUSED",
128
+ ].includes(code);
129
+
130
+ return error;
131
+ }
132
+
133
+ /**
134
+ * Main error handler that processes any error and returns a standardized ErlcError
135
+ * @param {Error|Response} error - The error to handle
136
+ * @param {Object} [errorData] - Additional error data if available
137
+ * @returns {ErlcError} Standardized ERLC error
138
+ */
139
+ async function processError(error, errorData = null) {
140
+ // If it's already an ErlcError, return as-is
141
+ if (error instanceof ErlcError) {
142
+ return error;
143
+ }
144
+
145
+ // If it's a Response object (from fetch), handle as API error
146
+ if (error && typeof error.status === "number") {
147
+ return handleApiError(error, errorData);
148
+ }
149
+
150
+ // Handle as network/system error
151
+ return handleNetworkError(error);
152
+ }
153
+
154
+ module.exports = {
155
+ handleApiError,
156
+ handleNetworkError,
157
+ processError,
158
+ };