Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions src/errors/base.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
export interface AssemblyErrorOptions {
/**
* Override the default message for this error class.
* Each subclass provides a sensible default, so this is rarely needed.
*/
message?: string;
/** The original error that caused this one, forwarded to native Error.cause. */
cause?: unknown;
/** Arbitrary extra context (e.g. raw response body) for debugging. */
details?: unknown;
}

interface AssemblyBaseErrorInit extends AssemblyErrorOptions {
message: string;
statusCode: number;
}

export class AssemblyError extends Error {
readonly statusCode: number;
readonly details?: unknown;

constructor(message: string, statusCode: number, details?: unknown) {
super(message);
constructor({ message, statusCode, cause, details }: AssemblyBaseErrorInit) {
super(message, { cause });
this.name = "AssemblyError";
this.statusCode = statusCode;
this.details = details;
Expand Down
14 changes: 8 additions & 6 deletions src/errors/connection.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type { AssemblyErrorOptions } from "src/errors/base";
import { AssemblyError } from "src/errors/base";

export class AssemblyConnectionError extends AssemblyError {
constructor(messageOverride?: string, details?: unknown) {
super(
messageOverride ??
constructor({ message, ...rest }: AssemblyErrorOptions = {}) {
super({
message:
message ??
"A network error occurred while connecting to the Assembly API",
503,
details
);
statusCode: 503,
...rest,
});
this.name = "AssemblyConnectionError";
}
}
14 changes: 8 additions & 6 deletions src/errors/forbidden.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { AssemblyError } from "src/errors/base";
import type { AssemblyErrorOptions } from "src/errors/base";

export class AssemblyForbiddenError extends AssemblyError {
constructor(messageOverride?: string, details?: unknown) {
super(
messageOverride ??
constructor({ message, ...rest }: AssemblyErrorOptions = {}) {
super({
message:
message ??
"Forbidden: the API key lacks permission to perform this action",
403,
details
);
statusCode: 403,
...rest,
});
this.name = "AssemblyForbiddenError";
}
}
3 changes: 3 additions & 0 deletions src/errors/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
export { AssemblyError } from "src/errors/base";
export type { AssemblyErrorOptions } from "src/errors/base";
export { AssemblyConnectionError } from "src/errors/connection";
export { AssemblyForbiddenError } from "src/errors/forbidden";
export { AssemblyInvalidTokenError } from "src/errors/invalid-token";
export { AssemblyMissingApiKeyError } from "src/errors/missing-api-key";
export { AssemblyNoTokenError } from "src/errors/no-token";
export { AssemblyNotFoundError } from "src/errors/not-found";
export { AssemblyRateLimitError } from "src/errors/rate-limit";
export type { AssemblyRateLimitErrorOptions } from "src/errors/rate-limit";
export { AssemblyResponseParseError } from "src/errors/response-parse";
export type { AssemblyResponseParseErrorOptions } from "src/errors/response-parse";
export { AssemblyServerError } from "src/errors/server";
export { AssemblyUnauthorizedError } from "src/errors/unauthorized";
export { AssemblyValidationError } from "src/errors/validation";
14 changes: 8 additions & 6 deletions src/errors/invalid-token.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { AssemblyError } from "src/errors/base";
import type { AssemblyErrorOptions } from "src/errors/base";

export class AssemblyInvalidTokenError extends AssemblyError {
constructor(messageOverride?: string, details?: unknown) {
super(
messageOverride ??
constructor({ message, ...rest }: AssemblyErrorOptions = {}) {
super({
message:
message ??
"The provided token could not be decrypted or has an invalid payload",
401,
details
);
statusCode: 401,
...rest,
});
this.name = "AssemblyInvalidTokenError";
}
}
13 changes: 7 additions & 6 deletions src/errors/missing-api-key.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AssemblyError } from "src/errors/base";
import type { AssemblyErrorOptions } from "src/errors/base";

export class AssemblyMissingApiKeyError extends AssemblyError {
constructor(messageOverride?: string, details?: unknown) {
super(
messageOverride ?? "Assembly API key is missing or empty",
400,
details
);
constructor({ message, ...rest }: AssemblyErrorOptions = {}) {
super({
message: message ?? "Assembly API key is missing or empty",
statusCode: 400,
...rest,
});
this.name = "AssemblyMissingApiKeyError";
}
}
14 changes: 8 additions & 6 deletions src/errors/no-token.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { AssemblyError } from "src/errors/base";
import type { AssemblyErrorOptions } from "src/errors/base";

export class AssemblyNoTokenError extends AssemblyError {
constructor(messageOverride?: string, details?: unknown) {
super(
messageOverride ??
constructor({ message, ...rest }: AssemblyErrorOptions = {}) {
super({
message:
message ??
"A token is required for this operation but was not provided",
400,
details
);
statusCode: 400,
...rest,
});
this.name = "AssemblyNoTokenError";
}
}
13 changes: 7 additions & 6 deletions src/errors/not-found.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AssemblyError } from "src/errors/base";
import type { AssemblyErrorOptions } from "src/errors/base";

export class AssemblyNotFoundError extends AssemblyError {
constructor(messageOverride?: string, details?: unknown) {
super(
messageOverride ?? "The requested resource was not found",
404,
details
);
constructor({ message, ...rest }: AssemblyErrorOptions = {}) {
super({
message: message ?? "The requested resource was not found",
statusCode: 404,
...rest,
});
this.name = "AssemblyNotFoundError";
}
}
21 changes: 15 additions & 6 deletions src/errors/rate-limit.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { AssemblyError } from "src/errors/base";
import type { AssemblyErrorOptions } from "src/errors/base";

export interface AssemblyRateLimitErrorOptions extends AssemblyErrorOptions {
retryAfter?: number;
}

export class AssemblyRateLimitError extends AssemblyError {
readonly retryAfter?: number;

constructor(
messageOverride?: string,
retryAfter?: number,
details?: unknown
) {
super(messageOverride ?? "Rate limit exceeded", 429, details);
constructor({
message,
retryAfter,
...rest
}: AssemblyRateLimitErrorOptions = {}) {
super({
message: message ?? "Rate limit exceeded",
statusCode: 429,
...rest,
});
this.name = "AssemblyRateLimitError";
this.retryAfter = retryAfter;
}
Expand Down
34 changes: 25 additions & 9 deletions src/errors/response-parse.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
import { AssemblyError } from "src/errors/base";
import type { AssemblyErrorOptions } from "src/errors/base";
import { z } from "zod";
import type { ZodError } from "zod";

export interface AssemblyResponseParseErrorOptions extends Omit<
AssemblyErrorOptions,
"cause"
> {
cause: ZodError;
}

export class AssemblyResponseParseError extends AssemblyError {
readonly zodError: ZodError;
readonly validationErrors: string;

constructor(zodError: ZodError, messageOverride?: string) {
super(
messageOverride ??
"Assembly API response did not match the expected schema",
500,
zodError
);
constructor({ message, cause, details }: AssemblyResponseParseErrorOptions) {
super({
cause,
details,
message:
message ?? "Assembly API response did not match the expected schema",
statusCode: 500,
});
this.name = "AssemblyResponseParseError";
this.zodError = zodError;
this.validationErrors = z.prettifyError(cause);
}

// Narrows the inherited Error.cause to ZodError — safe because the
// constructor enforces cause: ZodError at the call site.
override get cause(): ZodError {
return super.cause as ZodError;
}
}
13 changes: 7 additions & 6 deletions src/errors/server.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AssemblyError } from "src/errors/base";
import type { AssemblyErrorOptions } from "src/errors/base";

export class AssemblyServerError extends AssemblyError {
constructor(messageOverride?: string, details?: unknown) {
super(
messageOverride ?? "An unexpected error occurred on the Assembly server",
500,
details
);
constructor({ message, ...rest }: AssemblyErrorOptions = {}) {
super({
message: message ?? "An unexpected error occurred on the Assembly server",
statusCode: 500,
...rest,
});
this.name = "AssemblyServerError";
}
}
13 changes: 7 additions & 6 deletions src/errors/unauthorized.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AssemblyError } from "src/errors/base";
import type { AssemblyErrorOptions } from "src/errors/base";

export class AssemblyUnauthorizedError extends AssemblyError {
constructor(messageOverride?: string, details?: unknown) {
super(
messageOverride ?? "Unauthorized: the API key was rejected by Assembly",
401,
details
);
constructor({ message, ...rest }: AssemblyErrorOptions = {}) {
super({
message: message ?? "Unauthorized: the API key was rejected by Assembly",
statusCode: 401,
...rest,
});
this.name = "AssemblyUnauthorizedError";
}
}
14 changes: 8 additions & 6 deletions src/errors/validation.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { AssemblyError } from "src/errors/base";
import type { AssemblyErrorOptions } from "src/errors/base";

export class AssemblyValidationError extends AssemblyError {
constructor(messageOverride?: string, details?: unknown) {
super(
messageOverride ?? "The request payload was rejected by the Assembly API",
422,
details
);
constructor({ message, ...rest }: AssemblyErrorOptions = {}) {
super({
message:
message ?? "The request payload was rejected by the Assembly API",
statusCode: 422,
...rest,
});
this.name = "AssemblyValidationError";
}
}
Loading