|
1 | 1 | import { AsyncLocalStorage } from "node:async_hooks";
|
2 | 2 | import * as crypto from "node:crypto";
|
3 |
| -import type { Optional } from "utility-types"; |
4 | 3 |
|
5 | 4 | const asyncLocalStorage = new AsyncLocalStorage();
|
6 | 5 |
|
7 | 6 | let globalStore = {};
|
8 | 7 |
|
9 |
| -interface CreateHookOptions< |
10 |
| - State extends Record<string, any>, |
11 |
| - ExecuteResult, |
12 |
| - HookOptions |
13 |
| -> { |
| 8 | +interface CreateHookOptions<State, ExecuteResult, HookOptions> { |
14 | 9 | name: string;
|
15 | 10 | data: () => State;
|
16 | 11 | execute: (state: State, options: HookOptions) => ExecuteResult;
|
@@ -46,30 +41,84 @@ const generateUpdateHookStateFunction = <State, ExecuteResult, HookOptions>(
|
46 | 41 | };
|
47 | 42 | };
|
48 | 43 |
|
49 |
| -export const createHook = <State, ExecuteResult, HookOptions>( |
50 |
| - options: Optional< |
51 |
| - Omit<CreateHookOptions<State, ExecuteResult, HookOptions>, "name">, |
52 |
| - "data" |
53 |
| - > |
| 44 | +type StateAndExecuteOptions<State, ExecuteResult, HookOptions> = Omit< |
| 45 | + CreateHookOptions<State, ExecuteResult, HookOptions>, |
| 46 | + "name" |
| 47 | +>; |
| 48 | + |
| 49 | +type StateOnlyOptions<State, HookOptions> = Omit< |
| 50 | + StateAndExecuteOptions<State, undefined, HookOptions>, |
| 51 | + "execute" |
| 52 | +>; |
| 53 | + |
| 54 | +type ExecuteOnlyOptions<ExecuteResult, HookOptions> = Omit< |
| 55 | + StateAndExecuteOptions<undefined, ExecuteResult, HookOptions>, |
| 56 | + "data" |
| 57 | +>; |
| 58 | + |
| 59 | +export function createHook<State, ExecuteResult, HookOptions>( |
| 60 | + options: StateAndExecuteOptions<State, ExecuteResult, HookOptions> |
54 | 61 | ): [
|
55 | 62 | (parameters?: HookOptions) => ExecuteResult,
|
56 | 63 | (fn: (currentState: State) => State) => void
|
57 |
| -] => { |
| 64 | +]; |
| 65 | +export function createHook<State, HookOptions>( |
| 66 | + options: StateOnlyOptions<State, HookOptions> |
| 67 | +): [() => State, (fn: (currentState: State) => State) => void]; |
| 68 | +export function createHook<ExecuteResult, HookOptions>( |
| 69 | + options: ExecuteOnlyOptions<ExecuteResult, HookOptions> |
| 70 | +): [(parameters?: HookOptions) => ExecuteResult, (fn: () => void) => void]; |
| 71 | +export function createHook<State, ExecuteResult, HookOptions>( |
| 72 | + options: |
| 73 | + | StateOnlyOptions<State, HookOptions> |
| 74 | + | ExecuteOnlyOptions<ExecuteResult, HookOptions> |
| 75 | + | StateAndExecuteOptions<State, ExecuteResult, HookOptions> |
| 76 | +) { |
58 | 77 | const name = crypto.randomUUID();
|
59 |
| - const data = options.data || (() => ({} as State)); |
| 78 | + if ("data" in options && !("execute" in options)) { |
| 79 | + const execute = (state: State) => state; |
| 80 | + return [ |
| 81 | + generateHookFunction({ |
| 82 | + name, |
| 83 | + data: options.data, |
| 84 | + execute, |
| 85 | + }), |
| 86 | + generateUpdateHookStateFunction({ |
| 87 | + name, |
| 88 | + data: options.data, |
| 89 | + execute, |
| 90 | + }), |
| 91 | + ]; |
| 92 | + } |
| 93 | + if ("data" in options) { |
| 94 | + return [ |
| 95 | + generateHookFunction({ |
| 96 | + name, |
| 97 | + data: options.data, |
| 98 | + execute: options.execute, |
| 99 | + }), |
| 100 | + generateUpdateHookStateFunction({ |
| 101 | + name, |
| 102 | + data: options.data, |
| 103 | + execute: options.execute, |
| 104 | + }), |
| 105 | + ]; |
| 106 | + } |
| 107 | + |
| 108 | + const data = () => undefined; |
60 | 109 | return [
|
61 | 110 | generateHookFunction({
|
62 |
| - ...options, |
63 | 111 | name,
|
64 | 112 | data,
|
| 113 | + execute: options.execute, |
65 | 114 | }),
|
66 | 115 | generateUpdateHookStateFunction({
|
67 |
| - ...options, |
68 | 116 | name,
|
69 | 117 | data,
|
| 118 | + execute: options.execute, |
70 | 119 | }),
|
71 | 120 | ];
|
72 |
| -}; |
| 121 | +} |
73 | 122 |
|
74 | 123 | export const runHookContext = async <T>(
|
75 | 124 | fn: () => Promise<T> | T
|
|
0 commit comments