standard-rule-engine
Version:
A simple rule engine that uses Standard Schema to validate facts
110 lines (106 loc) • 4.65 kB
text/typescript
import { StandardSchemaV1 } from '@standard-schema/spec';
interface EngineSingletonBase {
context: Record<string, unknown>;
globalSchema: StandardSchemaV1 | undefined;
helpers: Record<string, Function>;
}
type Prettify<T> = {
[K in keyof T]: T[K];
} & {};
type RecordKey = string | number | symbol;
type Reconcile<A extends Object, B extends Object, Override extends boolean = false, Stack extends number[] = []> = Stack["length"] extends 16 ? A : Override extends true ? {
[key in keyof A as key extends keyof B ? never : key]: A[key];
} extends infer Collision ? {} extends Collision ? {
[key in keyof B]: IsBothObject<A[key], B[key]> extends true ? Reconcile<A[key], B[key], Override, [
0,
...Stack
]> : B[key];
} : Prettify<Collision & {
[key in keyof B]: B[key];
}> : never : {
[key in keyof B as key extends keyof A ? never : key]: B[key];
} extends infer Collision ? {} extends Collision ? {
[key in keyof A]: IsBothObject<A[key], B[key]> extends true ? Reconcile<A[key], B[key], Override, [
0,
...Stack
]> : A[key];
} : Prettify<{
[key in keyof A]: A[key];
} & Collision> : never;
type IsBothObject<A, B> = A extends Record<RecordKey, unknown> ? B extends Record<RecordKey, unknown> ? IsClass<A> extends false ? IsClass<B> extends false ? true : false : false : false : false;
type IsClass<V> = V extends abstract new (...args: any) => any ? true : false;
type DeepReadonly<T> = T extends Function ? T : T extends Array<infer U> ? ReadonlyArray<DeepReadonly<U>> : T extends object ? {
readonly [P in keyof T]: DeepReadonly<T[P]>;
} : T;
type AnyEngine = Engine<any>;
type Rule = {
name: string;
priority: number;
handler: (...args: any[]) => void;
schema?: StandardSchemaV1;
};
declare class Session<Context extends Record<string, unknown>, Helpers extends Record<string, Function>> {
context: Context;
private rules;
private insertedFacts;
private wrappedHelpers;
constructor(context: Context, rules: Rule[], helpers?: Helpers);
insert(facts: unknown): this;
insertMany(facts: unknown[]): this;
fire(): this;
}
declare class Engine<const in out Singleton extends EngineSingletonBase = {
context: {};
globalSchema: undefined;
helpers: {};
}> {
"~types": {
Singleton: Singleton;
};
private initialContext;
private rules;
private globalSchema;
private helpers;
schema<const GlobalFactsSchema extends StandardSchemaV1>(schema: GlobalFactsSchema): Engine<{
context: Singleton["context"];
globalSchema: GlobalFactsSchema;
helpers: Singleton["helpers"];
}>;
context<const Name extends string | number | symbol, Value>(name: Name, value: Value): Engine<{
context: Reconcile<Singleton["context"], {
[key in Name]: Value;
}>;
globalSchema: Singleton["globalSchema"];
helpers: Singleton["helpers"];
}>;
context<IncomingContext extends Record<string, unknown>>(context: IncomingContext): Engine<{
context: Reconcile<Singleton["context"], IncomingContext>;
globalSchema: Singleton["globalSchema"];
helpers: Singleton["helpers"];
}>;
helper<const Name extends string, Args extends any[], ReturnType>(name: Name, fn: (context: Singleton["context"], ...args: Args) => ReturnType): Engine<{
context: Singleton["context"];
globalSchema: Singleton["globalSchema"];
helpers: Singleton["helpers"] & {
[key in Name]: (...args: Args) => ReturnType;
};
}>;
rule<const RuleName extends string, const FactsSchema extends Singleton["globalSchema"] extends undefined ? StandardSchemaV1 | undefined : Singleton["globalSchema"]>(name: RuleName, handler: (facts: DeepReadonly<FactsSchema extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<FactsSchema> : unknown>, { context, helpers, }: {
context: Singleton["context"];
helpers: Singleton["helpers"];
}) => void, meta?: {
schema?: FactsSchema;
priority?: number;
}): Engine<{
context: Singleton["context"];
globalSchema: Singleton["globalSchema"];
helpers: Singleton["helpers"];
}>;
use<const NewEngine extends AnyEngine>(instance: NewEngine): Engine<{
context: Singleton["context"] & NewEngine["~types"]["Singleton"]["context"];
globalSchema: Singleton["globalSchema"];
helpers: Singleton["helpers"] & NewEngine["~types"]["Singleton"]["helpers"];
}>;
createSession(): Session<Singleton["context"], Singleton["helpers"]>;
}
export { type AnyEngine, Engine };