@rivetkit/core
Version:
361 lines (319 loc) • 9.01 kB
text/typescript
import type { DeconstructedError } from "@/common/utils";
export const INTERNAL_ERROR_CODE = "internal_error";
export const INTERNAL_ERROR_DESCRIPTION =
"Internal error. Read the server logs for more details.";
export type InternalErrorMetadata = {};
export const USER_ERROR_CODE = "user_error";
interface ActorErrorOptions extends ErrorOptions {
/** Error data can safely be serialized in a response to the client. */
public?: boolean;
/** Metadata associated with this error. This will be sent to clients. */
metadata?: unknown;
}
export class ActorError extends Error {
__type = "ActorError";
public public: boolean;
public metadata?: unknown;
public statusCode = 500;
public static isActorError(
error: unknown,
): error is ActorError | DeconstructedError {
return (
typeof error === "object" &&
(error as ActorError | DeconstructedError).__type === "ActorError"
);
}
constructor(
public readonly code: string,
message: string,
opts?: ActorErrorOptions,
) {
super(message, { cause: opts?.cause });
this.public = opts?.public ?? false;
this.metadata = opts?.metadata;
// Set status code based on error type
if (opts?.public) {
this.statusCode = 400; // Bad request for public errors
}
}
toString() {
// Force stringify to return the message
return this.message;
}
/**
* Serialize error for HTTP response
*/
serializeForHttp() {
return {
type: this.code,
message: this.message,
metadata: this.metadata,
};
}
}
export class InternalError extends ActorError {
constructor(message: string) {
super(INTERNAL_ERROR_CODE, message);
}
}
export class Unreachable extends InternalError {
constructor(x: never) {
super(`Unreachable case: ${x}`);
}
}
export class StateNotEnabled extends ActorError {
constructor() {
super(
"state_not_enabled",
"State not enabled. Must implement `createState` or `state` to use state. (https://www.rivet.gg/docs/actors/state/#initializing-state)",
);
}
}
export class ConnStateNotEnabled extends ActorError {
constructor() {
super(
"conn_state_not_enabled",
"Connection state not enabled. Must implement `createConnectionState` or `connectionState` to use connection state. (https://www.rivet.gg/docs/actors/connections/#connection-state)",
);
}
}
export class VarsNotEnabled extends ActorError {
constructor() {
super(
"vars_not_enabled",
"Variables not enabled. Must implement `createVars` or `vars` to use state. (https://www.rivet.gg/docs/actors/ephemeral-variables/#initializing-variables)",
);
}
}
export class ActionTimedOut extends ActorError {
constructor() {
super(
"action_timed_out",
"Action timed out. This can be increased with: `actor({ options: { action: { timeout: ... } } })`",
{ public: true },
);
}
}
export class ActionNotFound extends ActorError {
constructor(name: string) {
super(
"action_not_found",
`Action '${name}' not found. Validate the action exists on your actor.`,
{ public: true },
);
}
}
export class InvalidEncoding extends ActorError {
constructor(format?: string) {
super(
"invalid_encoding",
`Invalid encoding \`${format}\`. (https://www.rivet.gg/docs/actors/clients/#actor-client)`,
{
public: true,
},
);
}
}
export class ConnNotFound extends ActorError {
constructor(id?: string) {
super("conn_not_found", `Connection not found for ID: ${id}`, {
public: true,
});
}
}
export class IncorrectConnToken extends ActorError {
constructor() {
super("incorrect_conn_token", "Incorrect connection token.", {
public: true,
});
}
}
export class MessageTooLong extends ActorError {
constructor() {
super(
"message_too_long",
"Message too long. This can be configured with: `registry.runServer({ maxIncomingMessageSize: ... })`",
{ public: true },
);
}
}
export class MalformedMessage extends ActorError {
constructor(cause?: unknown) {
super("malformed_message", `Malformed message: ${cause}`, {
public: true,
cause,
});
}
}
export interface InvalidStateTypeOptions {
path?: unknown;
}
export class InvalidStateType extends ActorError {
constructor(opts?: InvalidStateTypeOptions) {
let msg = "";
if (opts?.path) {
msg += `Attempted to set invalid state at path \`${opts.path}\`.`;
} else {
msg += "Attempted to set invalid state.";
}
msg +=
" Valid types include: null, undefined, boolean, string, number, BigInt, Date, RegExp, Error, typed arrays (Uint8Array, Int8Array, Float32Array, etc.), Map, Set, Array, and plain objects. (https://www.rivet.gg/docs/actors/state/#limitations)";
super("invalid_state_type", msg);
}
}
export class Unsupported extends ActorError {
constructor(feature: string) {
super("unsupported", `Unsupported feature: ${feature}`);
}
}
/**
* Options for the UserError class.
*/
export interface UserErrorOptions extends ErrorOptions {
/**
* Machine readable code for this error. Useful for catching different types of errors in try-catch.
*/
code?: string;
/**
* Additional metadata related to the error. Useful for understanding context about the error.
*/
metadata?: unknown;
}
/** Error that can be safely returned to the user. */
export class UserError extends ActorError {
/**
* Constructs a new UserError instance.
*
* @param message - The error message to be displayed.
* @param opts - Optional parameters for the error, including a machine-readable code and additional metadata.
*/
constructor(message: string, opts?: UserErrorOptions) {
super(opts?.code ?? USER_ERROR_CODE, message, {
public: true,
metadata: opts?.metadata,
});
}
}
export class InvalidQueryJSON extends ActorError {
constructor(error?: unknown) {
super("invalid_query_json", `Invalid query JSON: ${error}`, {
public: true,
cause: error,
});
}
}
export class InvalidRequest extends ActorError {
constructor(error?: unknown) {
super("invalid_request", `Invalid request: ${error}`, {
public: true,
cause: error,
});
}
}
export class ActorNotFound extends ActorError {
constructor(identifier?: string) {
super(
"actor_not_found",
identifier
? `Actor not found: ${identifier} (https://www.rivet.gg/docs/actors/clients/#actor-client)`
: "Actor not found (https://www.rivet.gg/docs/actors/clients/#actor-client)",
{ public: true },
);
}
}
export class ActorAlreadyExists extends ActorError {
constructor(name: string, key: string[]) {
super(
"actor_already_exists",
`Actor already exists with name '${name}' and key '${JSON.stringify(key)}' (https://www.rivet.gg/docs/actors/clients/#actor-client)`,
{ public: true },
);
}
}
export class ProxyError extends ActorError {
constructor(operation: string, error?: unknown) {
super(
"proxy_error",
`Error proxying ${operation}, this is likely an internal error: ${error}`,
{
public: true,
cause: error,
},
);
}
}
export class InvalidActionRequest extends ActorError {
constructor(message: string) {
super("invalid_action_request", message, { public: true });
}
}
export class InvalidParams extends ActorError {
constructor(message: string) {
super("invalid_params", message, { public: true });
}
}
export class Unauthorized extends ActorError {
constructor(message?: string) {
super(
"unauthorized",
message ??
"Unauthorized. Access denied. (https://www.rivet.gg/docs/actors/authentication/)",
{
public: true,
},
);
this.statusCode = 401;
}
}
export class Forbidden extends ActorError {
constructor(message?: string, opts?: { metadata?: unknown }) {
super(
"forbidden",
message ??
"Forbidden. Access denied. (https://www.rivet.gg/docs/actors/authentication/)",
{
public: true,
metadata: opts?.metadata,
},
);
this.statusCode = 403;
}
}
export class DatabaseNotEnabled extends ActorError {
constructor() {
super(
"database_not_enabled",
"Database not enabled. Must implement `database` to use database.",
);
}
}
export class FetchHandlerNotDefined extends ActorError {
constructor() {
super(
"fetch_handler_not_defined",
"Raw HTTP handler not defined. Actor must implement `onFetch` to handle raw HTTP requests. (https://www.rivet.gg/docs/actors/fetch-and-websocket-handler/)",
{ public: true },
);
this.statusCode = 404;
}
}
export class WebSocketHandlerNotDefined extends ActorError {
constructor() {
super(
"websocket_handler_not_defined",
"Raw WebSocket handler not defined. Actor must implement `onWebSocket` to handle raw WebSocket connections. (https://www.rivet.gg/docs/actors/fetch-and-websocket-handler/)",
{ public: true },
);
this.statusCode = 404;
}
}
export class InvalidFetchResponse extends ActorError {
constructor() {
super(
"invalid_fetch_response",
"Actor's onFetch handler must return a Response object. Returning void/undefined is not allowed. (https://www.rivet.gg/docs/actors/fetch-and-websocket-handler/)",
{ public: true },
);
this.statusCode = 500;
}
}