Skip to content

Commit 5d7b0e9

Browse files
author
Wojciech Wierchola
committed
add abort + fix git
1 parent 5730c5a commit 5d7b0e9

File tree

4 files changed

+97
-17
lines changed

4 files changed

+97
-17
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ node_modules
22
browser
33
*.js
44
*.js.map
5-
*.d.ts
5+
*.d.ts
6+
!index.d.ts

browser.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,25 @@ export const makeApi = <Data extends ApiData>({
2020
Object.keys(headers).forEach(key => fetchHeaders.set(key, headers[key]));
2121
}
2222
}
23-
return (action, payload) =>
24-
fetch(endpoint, {
25-
method: "POST",
26-
headers: fetchHeaders,
27-
body: JSON.stringify({
28-
action,
29-
payload
30-
})
31-
}).then(r => r.json());
23+
return (action, payload) => {
24+
try {
25+
const controller = new AbortController();
26+
const signal = controller.signal;
27+
const promise = fetch(endpoint, {
28+
signal,
29+
method: "POST",
30+
headers: fetchHeaders,
31+
body: JSON.stringify({
32+
action,
33+
payload
34+
})
35+
}).then(r => r.json());
36+
Object.defineProperty(promise, "abort", {
37+
value: () => controller.abort()
38+
});
39+
return promise;
40+
} catch (err) {
41+
return Promise.reject(err);
42+
}
43+
};
3244
};

index.d.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
export type ActionFunction<Payload = any, Output = any, Context = any> = (
2+
payload: Payload,
3+
context: Context
4+
) => Promise<Output>;
5+
6+
export type Unpacked<T> = T extends Promise<infer U> ? U : T;
7+
8+
export type Payload<T extends ActionFunction> = T extends (
9+
p: infer P,
10+
c: any
11+
) => any
12+
? P
13+
: never;
14+
15+
export type Output<T extends ActionFunction> = T extends (
16+
p: any,
17+
c: any
18+
) => infer R
19+
? R
20+
: never;
21+
22+
export type ApiData = { [key: string]: ActionFunction };
23+
24+
export type PromiseA<T> = Promise<T> & {
25+
abort?: () => void;
26+
};
27+
28+
export type ApiResolver<D extends ApiData> = <N extends keyof D>(
29+
name: N,
30+
payload: Payload<D[N]>
31+
) => PromiseA<Unpacked<Output<D[N]>>>;

node.ts

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,51 @@
1-
import { ApiResolver, ApiData } from "@webcarrot/api";
1+
import {
2+
ApiResolver,
3+
ApiData,
4+
Payload,
5+
PromiseA,
6+
Unpacked,
7+
Output
8+
} from "@webcarrot/api";
9+
10+
const makeError = () => {
11+
const error = new Error("Action aborted");
12+
error.name === "AbortError";
13+
return error;
14+
};
215

316
export const makeApi = <Data extends ApiData, Context>({
417
actions,
518
context
619
}: {
720
actions: Data;
821
context: Context;
9-
}): ApiResolver<Data> => async (action, payload) => {
10-
if (action in actions) {
11-
return await actions[action](payload, context);
12-
} else {
13-
throw new Error(`Unknown action ${action}`);
14-
}
22+
}): ApiResolver<Data> => <N extends keyof Data>(
23+
action: N,
24+
payload: Payload<Data[N]>
25+
): PromiseA<Unpacked<Output<Data[N]>>> => {
26+
let aborted = false;
27+
const promise = new Promise<Unpacked<Output<Data[N]>>>((resolve, reject) => {
28+
if (aborted) {
29+
reject(makeError());
30+
} else if (action in actions) {
31+
actions[action](payload, context).then(
32+
data => {
33+
if (!aborted) {
34+
resolve(data);
35+
} else {
36+
reject(makeError());
37+
}
38+
},
39+
err => reject(aborted ? makeError() : err)
40+
);
41+
} else {
42+
reject(new Error(`Unknown action ${action}`));
43+
}
44+
});
45+
Object.defineProperty(promise, "abort", {
46+
value: () => {
47+
aborted = true;
48+
}
49+
});
50+
return promise;
1551
};

0 commit comments

Comments
 (0)