Skip to content

Commit

Permalink
move YumiError creator out of YumiError class, handle opaque response…
Browse files Browse the repository at this point in the history
… type and stop using defineForClassFields in ts
  • Loading branch information
MellKam committed Jul 5, 2023
1 parent 40ad065 commit 95131cf
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 66 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "yumi-fetch",
"version": "0.2.3",
"version": "0.2.4",
"description": "🍭 An extensible fetch wrapper for simplified and powerful HTTP requests using native web APIs",
"main": "dist/index.cjs",
"module": "dist/index.js",
Expand Down
4 changes: 2 additions & 2 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
mergeHeaders,
mergeURLs,
} from "./utils.ts";
import { HTTPError, YumiError } from "./http_error.ts";
import { createYumiError, HTTPError } from "./http_error.ts";

export type RequestOptions<T_RequestOptions = unknown> =
& RequestInit
Expand Down Expand Up @@ -406,7 +406,7 @@ export const clientCore: Client = {
);
},
_linkedFetchStale: false,
_errorCreator: YumiError.create,
_errorCreator: createYumiError,
withErrorCreator(errorCreator) {
return {
...this,
Expand Down
18 changes: 9 additions & 9 deletions src/http_error.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isHTTPError, YumiError } from "./http_error.ts";
import { createYumiError, isHTTPError } from "./http_error.ts";
import {
assert,
assertEquals,
Expand All @@ -7,7 +7,7 @@ import {
Deno.test("YumiError", async () => {
const req = new Request("http://example.com");
const res = new Response(JSON.stringify({ foo: "bar" }), { status: 500 });
const error = await YumiError.create(req, res);
const error = await createYumiError(req, res);

assertEquals(error.body, { foo: "bar" });
assert(error.status === 500);
Expand All @@ -19,10 +19,10 @@ Deno.test("YumiError", async () => {
Deno.test("YumiError - without body", async () => {
const req = new Request("http://example.com");
const res = new Response(null, { status: 400 });
const error = await YumiError.create(req, res);
const error = await createYumiError(req, res);

assert(typeof error.body === "undefined");
assert(error.message === "400 Unknown error");
assert(error.message === "Unknown error");
assert(error.status === 400);
assertEquals(error.request, req);
assertEquals(error.response, res);
Expand All @@ -31,24 +31,24 @@ Deno.test("YumiError - without body", async () => {
Deno.test("YumiError - with body as string", async () => {
const req = new Request("http://example.com");
const res = new Response("Bad request", { status: 400 });
const error = await YumiError.create(req, res);
const error = await createYumiError(req, res);

console.log(error.message);
assert(error.message === "400 Bad request");
assert(error.message === "Bad request");
});

Deno.test("YumiError - with string body", async () => {
const req = new Request("http://example.com");
const res = new Response(null, { status: 400, statusText: "Bad request" });
const error = await YumiError.create(req, res);
const error = await createYumiError(req, res);

assert(error.message === "400 Bad request");
assert(error.message === "Bad request");
});

Deno.test("isHTTPError", async () => {
const req = new Request("http://example.com");
const res = new Response(null, { status: 400 });

assert(isHTTPError(await YumiError.create(req, res)));
assert(isHTTPError(await createYumiError(req, res)));
assert(isHTTPError(new Error("Not a HTTPError")) === false);
});
60 changes: 34 additions & 26 deletions src/http_error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,53 +32,61 @@ export const isHTTPError = (err: unknown): err is HTTPError => {
};

export class YumiError<T_Body = unknown> extends Error implements HTTPError {
public readonly name = "YumiError";
public readonly status: number;
public readonly request: Request;
public readonly response: Response;
public readonly body: T_Body;

constructor(
message: string,
request: Request,
response: Response,
body: T_Body,
options?: ErrorOptions,
) {
const message = typeof body === "string"
? body
: body
? JSON.stringify(body)
: response.statusText || "Unknown error";
super(response.status + " " + message, options);

super(message, options);
this.request = request;
this.response = response;
this.body = body;
this.status = response.status;
this.name = "YumiError";
}

get url() {
return this.response.url;
}
}

static async create<T_Body = unknown>(
request: Request,
response: Response,
options?: ErrorOptions,
) {
let body: T_Body = undefined as T_Body;

if (!response.body) {
return new YumiError<T_Body>(request, response, body, options);
}
export const createYumiError = async <T_Body = unknown>(
request: Request,
response: Response,
options?: ErrorOptions,
) => {
const fallbackMessage = response.statusText || "Unknown error";
let body: T_Body = undefined as T_Body;

try {
body = (await response.text()) as T_Body;
body = JSON.parse(body as string);
} catch (_) {
/* Ignore errors */
}
if (!response.body || response.type === "opaque") {
return new YumiError<T_Body>(
fallbackMessage,
request,
response,
body,
options,
);
}

return new YumiError<T_Body>(request, response, body, options);
try {
body = (await response.text()) as T_Body;
body = JSON.parse(body as string);
} catch (_) {
/* Ignore errors */
}
}

const message = typeof body === "string"
? body
: body
? JSON.stringify(body)
: fallbackMessage;

return new YumiError<T_Body>(message, request, response, body, options);
};
57 changes: 29 additions & 28 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
{
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"allowImportingTsExtensions": true,
"noImplicitAny": true,
"noEmitOnError": true,
"noImplicitReturns": true,
"noUnusedParameters": true,
"emitDeclarationOnly": true,
"noUncheckedIndexedAccess": true,
"noImplicitThis": true,
"declaration": true,
"strict": true,
"removeComments": false,
"lib": ["DOM", "ESNext", "DOM.Iterable"],
"noUnusedLocals": true,
"moduleResolution": "NodeNext",
"module": "ESNext",
"target": "ESNext",
"outDir": "dist",
"baseUrl": ".",
"isolatedModules": true,
"ignoreDeprecations": "5.0",
"allowUnreachableCode": false,
"allowUnusedLabels": false
},
"include": ["./src/**/*.ts"],
"exclude": ["node_modules", "./src/**/*.demo.ts", "./src/**/*.test.ts"]
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"allowImportingTsExtensions": true,
"noImplicitAny": true,
"noEmitOnError": true,
"noImplicitReturns": true,
"noUnusedParameters": true,
"emitDeclarationOnly": true,
"noUncheckedIndexedAccess": true,
"noImplicitThis": true,
"declaration": true,
"strict": true,
"removeComments": false,
"lib": ["DOM", "ESNext", "DOM.Iterable"],
"noUnusedLocals": true,
"moduleResolution": "NodeNext",
"module": "ESNext",
"target": "ESNext",
"outDir": "dist",
"baseUrl": ".",
"isolatedModules": true,
"ignoreDeprecations": "5.0",
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"useDefineForClassFields": false
},
"include": ["./src/**/*.ts"],
"exclude": ["node_modules", "./src/**/*.demo.ts", "./src/**/*.test.ts"]
}

0 comments on commit 95131cf

Please sign in to comment.