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
4 changes: 2 additions & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ jobs:
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: 20
node-version: 24
registry-url: https://registry.npmjs.org/
cache: 'pnpm'

Expand Down
7 changes: 3 additions & 4 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,19 @@ jobs:
test:
strategy:
matrix:
version: [20, 22]
version: [24]
os: [ubuntu-latest]

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.version }}
registry-url: https://registry.npmjs.org/
cache: 'pnpm'

- run: make test publint
17 changes: 5 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,34 +1,27 @@

SRCS = $(wildcard lib/**)

all: dist
all: typecheck

.PHONY: clean distclean
.PHONY: distclean
distclean:
rm -rf node_modules

.PHONY: clean
clean: node_modules
pnpm exec tsc -b --clean

.PHONY: test
test: node_modules
pnpm exec vitest

node_modules: package.json
pnpm install

dist: node_modules tsconfig.json $(SRCS)
.PHONY: typecheck
typecheck: node_modules tsconfig.json $(SRCS)
pnpm exec tsc

.PHONY: dev
dev: node_modules
pnpm exec tsc -w

.PHONY: pretty
pretty: node_modules
pnpm exec prettier --write .

.PHONY: publint
publint: dist
publint: typecheck
npx publint --strict
68 changes: 38 additions & 30 deletions lib/custom-error.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,39 @@
import { isErrorLike } from "serialize-error";
import type { ErrorDetail, LocalisedMessage } from "./types.js";
import { withNullProto } from "./utils.js";
import type { ErrorDetail, LocalisedMessage } from "./types.ts";
import { withNullProto } from "./utils.ts";

export type DebugData = Record<string, unknown>;

enum StatusCode {
OK = 0,
CANCELLED = 1,
UNKNOWN = 2,
INVALID_ARGUMENT = 3,
DEADLINE_EXCEEDED = 4,
NOT_FOUND = 5,
ALREADY_EXISTS = 6,
PERMISSION_DENIED = 7,
RESOURCE_EXHAUSTED = 8,
FAILED_PRECONDITION = 9,
ABORTED = 10,
OUT_OF_RANGE = 11,
UNIMPLEMENTED = 12,
INTERNAL = 13,
UNAVAILABLE = 14,
DATA_LOSS = 15,
UNAUTHENTICATED = 16,
const StatusCode = {
OK: 0 as const,
CANCELLED: 1 as const,
UNKNOWN: 2 as const,
INVALID_ARGUMENT: 3 as const,
DEADLINE_EXCEEDED: 4 as const,
NOT_FOUND: 5 as const,
ALREADY_EXISTS: 6 as const,
PERMISSION_DENIED: 7 as const,
RESOURCE_EXHAUSTED: 8 as const,
FAILED_PRECONDITION: 9 as const,
ABORTED: 10 as const,
OUT_OF_RANGE: 11 as const,
UNIMPLEMENTED: 12 as const,
INTERNAL: 13 as const,
UNAVAILABLE: 14 as const,
DATA_LOSS: 15 as const,
UNAUTHENTICATED: 16 as const,
}

// just export the type, we CustomError.XX should be used for the actual code
export type { StatusCode };
export type StatusCode = typeof StatusCode[keyof typeof StatusCode];

type SerializedError<T extends StatusCode | number> = {
const statusCodes: ReadonlySet<number> = new Set(Object.values(StatusCode));

export function isStatusCode(value: unknown): value is StatusCode {
return typeof value === 'number' && statusCodes.has(value);
}

export type SerializedError<T extends StatusCode> = {
readonly debug?: DebugData;
readonly stack?: string;
readonly cause?: SerializedError<StatusCode>[];
Expand Down Expand Up @@ -60,17 +66,17 @@ export class CustomError extends Error {

static http = Object.freeze({
[CustomError.OK]: 200,
[CustomError.CANCELLED]: 299,
[CustomError.CANCELLED]: 499,
[CustomError.UNKNOWN]: 500,
[CustomError.INVALID_ARGUMENT]: 400,
[CustomError.DEADLINE_EXCEEDED]: 504,
[CustomError.NOT_FOUND]: 404,
[CustomError.ALREADY_EXISTS]: 409,
[CustomError.PERMISSION_DENIED]: 403,
[CustomError.RESOURCE_EXHAUSTED]: 403,
[CustomError.RESOURCE_EXHAUSTED]: 429,
[CustomError.FAILED_PRECONDITION]: 400,
[CustomError.ABORTED]: 299,
[CustomError.OUT_OF_RANGE]: 400,
[CustomError.ABORTED]: 409,
[CustomError.OUT_OF_RANGE]: 422,
[CustomError.UNIMPLEMENTED]: 501,
[CustomError.INTERNAL]: 500,
[CustomError.UNAVAILABLE]: 503,
Expand Down Expand Up @@ -114,7 +120,7 @@ export class CustomError extends Error {
this.cause = cause;

// FF doesnt have captureStackTrace
if (Error.captureStackTrace) {
if ('captureStackTrace' in Error) {
Error.captureStackTrace(this, this.constructor);
}

Expand Down Expand Up @@ -189,13 +195,13 @@ export class CustomError extends Error {
/**
* "Hydrates" a previously serialised error object
*/
public static fromJSON<const T extends StatusCode | number>(
public static fromJSON<const T extends StatusCode>(
params: SerializedError<T>,
) {
const { message, details, code } = params;

class ImportedError extends CustomError {
override readonly code = code;
override readonly code = code as StatusCode;
}

const err = new ImportedError(message).debug({
Expand All @@ -215,9 +221,11 @@ export class CustomError extends Error {
public static suggestHttpResponseCode(err: Error | CustomError | unknown) {
return (
(CustomError.isCustomError(err) && CustomError.http[err.code]) ||
CustomError.http[CustomError.UNKNOWN]
CustomError.http[CustomError.UNKNOWN] ||
500
);
}

}

// Mark all instances of 'CustomError'
Expand Down
9 changes: 0 additions & 9 deletions lib/index.ts

This file was deleted.

11 changes: 11 additions & 0 deletions lib/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { addKnownErrorConstructor } from "serialize-error";
import { CustomError } from "./custom-error.ts";

try {
addKnownErrorConstructor(CustomError);
} catch{}

export * from "./custom-error.ts";
export * from "./serialize.ts";

export type * from "./types.ts";
2 changes: 1 addition & 1 deletion lib/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
isErrorLike,
serializeError,
} from "serialize-error";
import { CustomError } from "./custom-error.js";
import { CustomError } from "./custom-error.ts";

function flatten(
err: unknown | ErrorLike | Error | CustomError,
Expand Down
31 changes: 12 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,35 +1,28 @@
{
"name": "@block65/custom-error",
"version": "13.0.0-rc.0",
"version": "14.0.0",
"private": false,
"license": "MIT",
"type": "module",
"exports": {
".": {
"types": "./dist/lib/index.d.ts",
"default": "./dist/lib/index.js"
}
},
"exports": "./lib/main.ts",
"files": [
"dist/lib/*.js",
"dist/lib/*.d.ts"
"lib"
],
"scripts": {
"preversion": "make clean && make test && make"
"preversion": "make test && make"
},
"dependencies": {
"serialize-error": "^12.0.0"
"serialize-error": "^13.0.1"
},
"devDependencies": {
"@tsconfig/node18": "^18.2.4",
"@tsconfig/strictest": "^2.0.5",
"vitest": "^2.1.8",
"@biomejs/biome": "^1.9.4",
"@types/node": "^20.17.12",
"typescript": "^5.7.3"
"@tsconfig/node24": "^24.0.4",
"@tsconfig/strictest": "^2.0.8",
"@types/node": "^24.0.0",
"typescript": "^5.8.0",
"vitest": "^3.0.0"
},
"engines": {
"node": ">=20.0.0"
"node": ">=24.0.0"
},
"packageManager": "pnpm@9.15.3+sha512.1f79bc245a66eb0b07c5d4d83131240774642caaa86ef7d0434ab47c0d16f66b04e21e0c086eb61e62c77efc4d7f7ec071afad3796af64892fae66509173893a"
"packageManager": "pnpm@10.30.3"
}
Loading