Vlad
·
2025-06-15
matcher.ts
1import type { AnyAction } from "./types.js";
2
3type ActionType = string;
4type GuardPredicate<G extends T, T = unknown> = (arg: T) => arg is G;
5type Predicate<Guard extends AnyAction = AnyAction> = (
6 action: Guard,
7) => boolean;
8type StringableActionCreator<A extends AnyAction = AnyAction> = {
9 (...args: any[]): A;
10 toString(): string;
11};
12type SubPattern<Guard extends AnyAction = AnyAction> =
13 | Predicate<Guard>
14 | StringableActionCreator
15 | ActionType;
16export type Pattern = SubPattern | SubPattern[];
17type ActionSubPattern<Guard extends AnyAction = AnyAction> =
18 | GuardPredicate<Guard, AnyAction>
19 | StringableActionCreator<Guard>
20 | Predicate<Guard>
21 | ActionType;
22export type ActionPattern<Guard extends AnyAction = AnyAction> =
23 | ActionSubPattern<Guard>
24 | ActionSubPattern<Guard>[];
25
26function isThunk(fn: any): boolean {
27 return (
28 typeof fn === "function" &&
29 typeof fn.run === "function" &&
30 typeof fn.use === "function" &&
31 typeof fn.name === "string" &&
32 typeof fn.toString === "function"
33 );
34}
35
36function isActionCreator(fn: any): boolean {
37 return !!fn && fn._starfx === true;
38}
39
40export function matcher(pattern: ActionPattern): Predicate {
41 if (pattern === "*") {
42 return (input) => !!input;
43 }
44
45 if (typeof pattern === "string") {
46 return (input) => pattern === input.type;
47 }
48
49 if (Array.isArray(pattern)) {
50 return (input) => pattern.some((p) => matcher(p)(input));
51 }
52
53 if (isThunk(pattern)) {
54 return (input) => pattern.toString() === input.type;
55 }
56
57 if (typeof pattern === "function" && !isActionCreator(pattern)) {
58 return (input) => pattern(input) as boolean;
59 }
60
61 if (isActionCreator(pattern)) {
62 return (input: AnyAction) => pattern.toString() === input.type;
63 }
64
65 throw new Error("invalid pattern");
66}