-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathsupervisor.ts
91 lines (81 loc) · 2.95 KB
/
supervisor.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
81
82
83
84
85
86
87
88
89
90
91
import { createAction, take } from "./action.ts";
import { call, Callable, Operation, race, sleep, spawn, Task } from "effection";
import type { ActionWithPayload, AnyAction } from "./types.ts";
import type { CreateActionPayload } from "./query/mod.ts";
import { getIdFromAction } from "./action.ts";
const MS = 1000;
const SECONDS = 1 * MS;
const MINUTES = 60 * SECONDS;
export function poll(parentTimer: number = 5 * SECONDS, cancelType?: string) {
return function* poller<T>(
actionType: string,
op: (action: AnyAction) => Operation<T>,
): Operation<T> {
const cancel = cancelType || actionType;
function* fire(action: { type: string }, timer: number) {
while (true) {
yield* op(action);
yield* sleep(timer);
}
}
while (true) {
const action = yield* take<{ timer?: number }>(actionType);
const timer = action.payload?.timer || parentTimer;
yield* race([fire(action, timer), take(`${cancel}`) as Operation<void>]);
}
};
}
type ClearTimerPayload = string | { type: string; payload: { key: string } };
export const clearTimers = createAction<
ClearTimerPayload | ClearTimerPayload[]
>("clear-timers");
/**
* timer() will create a cache timer for each `key` inside
* of a starfx api endpoint. `key` is a hash of the action type and payload.
*
* Why do we want this? If we have an api endpoint to fetch a single app: `fetchApp({ id: 1 })`
* if we don't set a timer per key then all calls to `fetchApp` will be on a timer.
* So if we call `fetchApp({ id: 1 })` and then `fetchApp({ id: 2 })` if we use a normal
* cache timer then the second call will not send an http request.
*/
export function timer(timer: number = 5 * MINUTES) {
return function* onTimer(
actionType: string,
op: (action: AnyAction) => Callable<unknown>,
) {
const map: { [key: string]: Task<unknown> } = {};
function* activate(action: ActionWithPayload<CreateActionPayload>) {
yield* call(() => op(action));
const idA = getIdFromAction(action);
const matchFn = (
act: ActionWithPayload<ClearTimerPayload | ClearTimerPayload[]>,
) => {
if (act.type !== `${clearTimers}`) return false;
if (!act.payload) return false;
const ids = Array.isArray(act.payload) ? act.payload : [act.payload];
return ids.some((id) => {
if (id === "*") {
return true;
}
if (typeof id === "string") {
return idA === id;
} else {
return idA === getIdFromAction(id);
}
});
};
yield* race([sleep(timer), take(matchFn as any) as Operation<void>]);
delete map[action.payload.key];
}
while (true) {
const action = yield* take<CreateActionPayload>(`${actionType}`);
const key = action.payload.key;
if (!map[key]) {
const task = yield* spawn(function* () {
yield* activate(action);
});
map[key] = task;
}
}
};
}