Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | 70x 70x 70x 70x 191x 191x 897x 897x 897x 761x 761x 897x 897x 897x 154x 897x | import type {
ProcedureResolverOptions,
UnsetMarker,
} from "@trpc/server/unstable-core-do-not-import";
import type { Options } from "dataloader";
import Dataloader from "dataloader";
import { doNothing } from "remeda";
import type { MaybePromise } from "~utils/types";
import type {
AuthorizedContext,
UnauthorizedContext,
} from "~web/handlers/context";
const SCHEDULE_DELAY = 100;
const CLEAR_CACHE_DELAY = 2000;
type DefaultValue<TValue, TFallback> = TValue extends UnsetMarker
? TFallback
: TValue;
type ProcedureResolver<
TContext,
TMeta,
TContextOverrides,
TInputOut,
TOutputParserIn,
$Output,
> = (
opts: ProcedureResolverOptions<TContext, TMeta, TContextOverrides, TInputOut>,
) => MaybePromise<
// If an output parser is defined, we need to return what the parser expects, otherwise we return the inferred type
DefaultValue<TOutputParserIn, $Output>
>;
type ProcedureResolverShort<C, I, O> = ProcedureResolver<
C,
unknown,
object,
I,
UnsetMarker,
O
>;
const defaultGetKey = <C extends UnauthorizedContext>(context: C): string => {
// there are no batched requests with no auth yet
/* c8 ignore start */
if ("auth" in context) {
return (context as AuthorizedContext).auth.userId;
}
return "anonymous";
/* c8 ignore stop */
};
export type BatchLoadContextFn<
C extends UnauthorizedContext,
I,
O,
E extends Error = Error,
> = (ctx: C) => (keys: readonly I[]) => PromiseLike<(O | E)[]>;
export const queueCallFactory = <
C extends UnauthorizedContext,
I,
O,
E extends Error = Error,
>(
batchLoadFn: BatchLoadContextFn<C, I, O, E>,
{
getKey,
...batchOpts
}: Options<I, O, string> & {
getKey?: (ctx: C) => string;
} = {},
): ProcedureResolverShort<C, I, O> => {
const dataloaderStorage: Record<
string,
{
dataloader: Dataloader<I, O, string>;
removeTimeoutId: ReturnType<typeof setTimeout>;
}
> = {};
return async (opts) => {
const context = opts.ctx as C;
const key = (getKey || defaultGetKey)(context);
const dataloaderObject = dataloaderStorage[key] || {
dataloader: new Dataloader<I, O, string>(
(inputs) => batchLoadFn(context)(inputs),
{
batchScheduleFn: (callback) => {
setTimeout(callback, SCHEDULE_DELAY);
},
cacheKeyFn: JSON.stringify,
// Disable cache on test runs - subsequent calls with different data are happening in tests
cache: !context.reqHeaders.get("x-test-id"),
// Undocumented `opts.path` property
name: (opts as unknown as { path: string }).path,
...batchOpts,
},
),
removeTimeoutId: setTimeout(doNothing(), 0),
};
dataloaderStorage[key] = dataloaderObject;
clearTimeout(dataloaderObject.removeTimeoutId);
dataloaderObject.removeTimeoutId = setTimeout(() => {
delete dataloaderStorage[key];
}, CLEAR_CACHE_DELAY);
return dataloaderObject.dataloader.load(opts.input as I);
};
};
|