Skip to content

Commit

Permalink
Split up and add some structure
Browse files Browse the repository at this point in the history
  • Loading branch information
BBB committed Oct 11, 2023
1 parent daa4232 commit 276ddbd
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 32 deletions.
9 changes: 9 additions & 0 deletions packages/fetch-result/src/IncomingHttpResponseUnknownIssue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class IncomingHttpResponseUnknownIssue<Body = unknown> {
name = "IncomingHttpResponseUnknownIssue" as const;

constructor(
public method: string,
public body: Body,
public inner: Error,
) {}
}
7 changes: 7 additions & 0 deletions packages/fetch-result/src/Invalid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class Invalid<T> {
protected constructor(public value: T) {}

static of<T>(value: T) {
return new Invalid(value);
}
}
30 changes: 30 additions & 0 deletions packages/fetch-result/src/NetworkErrorMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { SensibleSet } from "~/src/SensibleSet";
import { Pair } from "~/src/Pair";
import { Result } from "@ollierelph/result4t";
import { Invalid } from "~/src/Invalid";

const valid = SensibleSet.of(
Pair.of("Failed to fetch", "Chrome / Edge"),
Pair.of("NetworkError when attempting to fetch resource.", "Firefox"),
Pair.of("The Internet connection appears to be offline.", "Safari"),
Pair.of("Load failed", "Safari"),
);

export class NetworkErrorMessage {
protected constructor(public value: string) {}

static of(value: string): Result<NetworkErrorMessage, Invalid<string>> {
if (valid.contains((it) => it.first === value)) {
return Result.success(new NetworkErrorMessage(value));
}
return Result.failure(Invalid.of(value));
}

toString() {
return this.value;
}

equals(other: NetworkErrorMessage) {
return this.value === other.value;
}
}
3 changes: 3 additions & 0 deletions packages/fetch-result/src/OutgoingHttpRequestAborted.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class OutgoingHttpRequestAborted {
name = "OutgoingHttpRequestAborted" as const;
}
9 changes: 9 additions & 0 deletions packages/fetch-result/src/OutgoingHttpRequestFailed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class OutgoingHttpRequestFailed<Body = unknown> {
name = "OutgoingHttpRequestFailed" as const;

constructor(
public method: string,
public body: Body,
public inner: Error,
) {}
}
10 changes: 10 additions & 0 deletions packages/fetch-result/src/Pair.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export class Pair<First, Second> {
protected constructor(
public first: First,
public second: Second,
) {}

static of<First, Second>(first: First, second: Second) {
return new Pair(first, second);
}
}
27 changes: 27 additions & 0 deletions packages/fetch-result/src/RequestAbortedErrorMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { SensibleSet } from "~/src/SensibleSet";
import { Pair } from "~/src/Pair";
import { Result } from "@ollierelph/result4t";
import { Invalid } from "~/src/Invalid";

const valid = SensibleSet.of("The user aborted a request");

export class RequestAbortedErrorMessage {
protected constructor(public value: string) {}

static of(
value: string,
): Result<RequestAbortedErrorMessage, Invalid<string>> {
if (valid.has(value)) {
return Result.success(new RequestAbortedErrorMessage(value));
}
return Result.failure(Invalid.of(value));
}

toString() {
return this.value;
}

equals(other: RequestAbortedErrorMessage) {
return this.value === other.value;
}
}
24 changes: 24 additions & 0 deletions packages/fetch-result/src/SensibleSet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export class SensibleSet<T> {
protected constructor(private set: Set<T>) {}

static of<T>(...args: T[]) {
return new SensibleSet(new Set(args));
}

map<Out>(predicate: (it: T) => Out) {
return SensibleSet.of(...[...this.set].map(predicate));
}

contains(predicate: (it: T) => boolean) {
return [...this.set].reduce((agg, it) => {
if (agg) {
return agg;
}
return predicate(it);
}, false);
}

has(it: T) {
return this.set.has(it);
}
}
5 changes: 5 additions & 0 deletions packages/fetch-result/src/UnexpectedIssueThrown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export class UnexpectedIssueThrown {
name = "UnexpectedIssueThrown" as const;

constructor(inner: unknown) {}
}
40 changes: 8 additions & 32 deletions packages/fetch-result/src/fetchResult.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { URL } from "node:url";
import { TaskResult } from "@ollierelph/result4t";
import { NetworkErrorMessage } from "~/src/NetworkErrorMessage";
import { RequestAbortedErrorMessage } from "~/src/RequestAbortedErrorMessage";
import { OutgoingHttpRequestFailed } from "~/src/OutgoingHttpRequestFailed";
import { IncomingHttpResponseUnknownIssue } from "~/src/IncomingHttpResponseUnknownIssue";
import { OutgoingHttpRequestAborted } from "~/src/OutgoingHttpRequestAborted";
import { UnexpectedIssueThrown } from "~/src/UnexpectedIssueThrown";

interface OutgoingHttpInit {
method?: string;
Expand All @@ -9,13 +15,6 @@ interface OutgoingHttpInit {
headers?: HeadersInit;
}

const networkErrorMsgs = new Set([
"Failed to fetch", // Chrome & Edge
"NetworkError when attempting to fetch resource.", // Firefox
"The Internet connection appears to be offline.", // Safari
"Load failed", // Safari,
]);

interface ReadableStream<R = any> {}

declare var Blob: {
Expand Down Expand Up @@ -216,29 +215,6 @@ export type Fetch<Res extends IncomingHttpResponse = IncomingHttpResponse> = (
init?: OutgoingHttpConfig | undefined,
) => Promise<Res>;

class UnexpectedIssueThrown {
name = "UnexpectedIssueThrown" as const;
constructor(inner: unknown) {}
}
class OutgoingHttpRequestAborted {
name = "OutgoingHttpRequestAborted" as const;
}
class IncomingHttpResponseUnknownIssue<Body = unknown> {
name = "IncomingHttpResponseUnknownIssue" as const;
constructor(
public method: string,
public body: Body,
public inner: Error,
) {}
}
class OutgoingHttpRequestFailed<Body = unknown> {
name = "OutgoingHttpRequestFailed" as const;
constructor(
public method: string,
public body: Body,
public inner: Error,
) {}
}
type FetchFailure =
| OutgoingHttpRequestAborted
| IncomingHttpResponseUnknownIssue
Expand All @@ -257,10 +233,10 @@ export const fetchResult =
if (!(err instanceof Error)) {
return new UnexpectedIssueThrown(err);
}
if (err.message.includes("The user aborted a request")) {
if (RequestAbortedErrorMessage.of(err.message).isSuccess()) {
return new OutgoingHttpRequestAborted();
}
if (networkErrorMsgs.has(err.message)) {
if (NetworkErrorMessage.of(err.message).isSuccess()) {
return new OutgoingHttpRequestFailed(
init?.method || "GET",
input.toString(),
Expand Down
2 changes: 2 additions & 0 deletions packages/fetch-result/test/fetchResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ it("responds with an error", async () => {
const res = Response.error();
expect(await underTest(res)("/woo").run()).toEqual(Result.failure(res));
});

it;

0 comments on commit 276ddbd

Please sign in to comment.