-
Notifications
You must be signed in to change notification settings - Fork 0
/
backend.ts
80 lines (76 loc) · 2.38 KB
/
backend.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import createResolvable, { Resolvable } from "@josephg/resolvable";
export type BackendRequester = (url: string, init?: RequestInit) => Promise<Response>;
export interface BackendGate {
backend: BackendRequester;
blockPath(path: string): { undo: () => void };
sabotagePath(path: string): { undo: () => void };
}
const BACKEND_ORIGIN = process.env.NODE_ENV === "development" ? "http://localhost:3031" : "https://backend.example.com";
export async function createBackendConnection(): Promise<BackendRequester> {
const appId = await createAppId();
let sessionId: string | undefined;
return async (url, init?) => {
init = init ?? {};
init.headers = init.headers ?? {};
(init.headers as any)["X-Application-ID"] = appId;
if (sessionId) {
(init.headers as any)["X-Session-Id"] = sessionId;
}
const response = await fetch(BACKEND_ORIGIN + url, init);
const newSessionId = response.headers.get("X-Session-Id");
if (newSessionId) {
sessionId = newSessionId;
}
return response;
};
}
export function createBackendGate(backend: BackendRequester): BackendGate {
const blocked: { [path: string]: Array<Resolvable<void>> } = {};
const sabotaged = new Set<string>();
return {
backend: async (path, init?) => {
if (blocked[path]) {
const resolvable = createResolvable();
blocked[path].push(resolvable);
await resolvable;
}
if (sabotaged.has(path)) {
throw new Error(`Backend path '${path}' has been sabotaged for debugging purposes`);
}
return backend(path, init);
},
blockPath: (path) => {
blocked[path] = [];
return {
undo: () => {
for (const resolvable of blocked[path]) {
resolvable.resolve();
}
delete blocked[path];
},
};
},
sabotagePath: (path) => {
sabotaged.add(path);
return {
undo: () => {
sabotaged.delete(path);
},
};
},
};
}
async function createAppId() {
for (let attempt = 0; attempt < 5; ++attempt) {
try {
const appCreation = await fetch(BACKEND_ORIGIN + "/app/create", {
method: "POST",
});
const { appId } = await appCreation.json();
return appId;
} catch (err) {
await new Promise((resolve) => setTimeout(resolve, 500));
}
}
throw new Error(`Could not create backend app after 5 attempts`);
}