rulit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +545 -0
- package/dist/cli/ui-template.hbs +313 -0
- package/dist/cli/ui.d.mts +9 -0
- package/dist/cli/ui.d.ts +9 -0
- package/dist/cli/ui.js +311 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/cli/ui.mjs +277 -0
- package/dist/cli/ui.mjs.map +1 -0
- package/dist/index.d.mts +745 -0
- package/dist/index.d.ts +745 -0
- package/dist/index.js +1357 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1322 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +65 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,745 @@
|
|
|
1
|
+
import { ZodType } from 'zod';
|
|
2
|
+
|
|
3
|
+
type ConditionTrace$1 = {
|
|
4
|
+
label: string;
|
|
5
|
+
result: boolean;
|
|
6
|
+
left?: unknown;
|
|
7
|
+
op?: string;
|
|
8
|
+
right?: unknown;
|
|
9
|
+
children?: ConditionTrace$1[];
|
|
10
|
+
reasonCode?: string;
|
|
11
|
+
};
|
|
12
|
+
type ConditionMeta$1<Facts> = {
|
|
13
|
+
label: string;
|
|
14
|
+
reasonCode?: string;
|
|
15
|
+
kind?: "atomic" | "and" | "or" | "not";
|
|
16
|
+
children?: Condition$1<Facts>[];
|
|
17
|
+
};
|
|
18
|
+
type RuleTrace$1 = {
|
|
19
|
+
ruleId: string;
|
|
20
|
+
matched: boolean;
|
|
21
|
+
conditions: ConditionTrace$1[];
|
|
22
|
+
notes: string[];
|
|
23
|
+
durationMs?: number;
|
|
24
|
+
error?: string;
|
|
25
|
+
meta?: RuleMeta$1;
|
|
26
|
+
skippedReason?: string;
|
|
27
|
+
};
|
|
28
|
+
type Condition$1<Facts> = ((facts: Facts) => ConditionTrace$1) & {
|
|
29
|
+
meta?: ConditionMeta$1<Facts>;
|
|
30
|
+
};
|
|
31
|
+
type ActionContext$1<Facts, Effects> = {
|
|
32
|
+
facts: Facts;
|
|
33
|
+
effects: Effects;
|
|
34
|
+
trace: {
|
|
35
|
+
note: (message: string) => void;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
type RuleActivation$1 = "all" | "first";
|
|
39
|
+
type RuleMeta$1 = {
|
|
40
|
+
tags?: string[];
|
|
41
|
+
description?: string;
|
|
42
|
+
version?: string;
|
|
43
|
+
reasonCode?: string;
|
|
44
|
+
enabled?: boolean;
|
|
45
|
+
};
|
|
46
|
+
type EffectsMode$1 = "mutable" | "immutable";
|
|
47
|
+
type MergeStrategy$1 = "assign" | "deep";
|
|
48
|
+
type RunOptions$1 = {
|
|
49
|
+
activation?: RuleActivation$1;
|
|
50
|
+
effectsMode?: EffectsMode$1;
|
|
51
|
+
mergeStrategy?: MergeStrategy$1;
|
|
52
|
+
rollbackOnError?: boolean;
|
|
53
|
+
includeTags?: string[];
|
|
54
|
+
excludeTags?: string[];
|
|
55
|
+
};
|
|
56
|
+
type GraphNode$1 = {
|
|
57
|
+
id: string;
|
|
58
|
+
type: "ruleset" | "rule" | "condition";
|
|
59
|
+
label: string;
|
|
60
|
+
reasonCode?: string;
|
|
61
|
+
tags?: string[];
|
|
62
|
+
description?: string;
|
|
63
|
+
version?: string;
|
|
64
|
+
};
|
|
65
|
+
type GraphEdge$1 = {
|
|
66
|
+
from: string;
|
|
67
|
+
to: string;
|
|
68
|
+
};
|
|
69
|
+
type RulesetGraph$1 = {
|
|
70
|
+
nodes: GraphNode$1[];
|
|
71
|
+
edges: GraphEdge$1[];
|
|
72
|
+
};
|
|
73
|
+
type RunResult$1<Effects> = {
|
|
74
|
+
effects: Effects;
|
|
75
|
+
fired: string[];
|
|
76
|
+
trace: RuleTrace$1[];
|
|
77
|
+
explain: () => string;
|
|
78
|
+
};
|
|
79
|
+
type TraceRun$1 = {
|
|
80
|
+
id: string;
|
|
81
|
+
createdAt: number;
|
|
82
|
+
facts: unknown;
|
|
83
|
+
fired: string[];
|
|
84
|
+
trace: RuleTrace$1[];
|
|
85
|
+
};
|
|
86
|
+
type TelemetryAttributes$1 = Record<string, unknown>;
|
|
87
|
+
type TelemetrySpan = {
|
|
88
|
+
end: () => void;
|
|
89
|
+
recordException?: (error: unknown) => void;
|
|
90
|
+
setAttribute?: (key: string, value: unknown) => void;
|
|
91
|
+
setAttributes?: (attributes: TelemetryAttributes$1) => void;
|
|
92
|
+
};
|
|
93
|
+
type TelemetryAdapter$1 = {
|
|
94
|
+
startSpan: (name: string, attributes?: TelemetryAttributes$1) => TelemetrySpan;
|
|
95
|
+
};
|
|
96
|
+
type ActionResult<Effects> = void | Partial<Effects>;
|
|
97
|
+
type AsyncActionResult<Effects> = ActionResult<Effects> | Promise<ActionResult<Effects>>;
|
|
98
|
+
|
|
99
|
+
type Primitive = string | number | boolean | bigint | symbol | null | undefined;
|
|
100
|
+
type DotPrefix<T extends string> = T extends "" ? "" : `.${T}`;
|
|
101
|
+
type PathImpl<T, Key extends keyof T> = Key extends string ? T[Key] extends Primitive ? Key : T[Key] extends Array<unknown> ? Key : T[Key] extends Date ? Key : Key | `${Key}${DotPrefix<Path$1<T[Key]>>}` : never;
|
|
102
|
+
type Path$1<T> = T extends object ? {
|
|
103
|
+
[K in keyof T]-?: PathImpl<T, K>;
|
|
104
|
+
}[keyof T] : never;
|
|
105
|
+
type PathValue$1<T, P extends Path$1<T>> = P extends `${infer Key}.${infer Rest}` ? Key extends keyof T ? Rest extends Path$1<T[Key]> ? PathValue$1<T[Key], Rest> : never : never : P extends keyof T ? T[P] : never;
|
|
106
|
+
type FieldOperators<T, P extends Path$1<T>> = PathValue$1<T, P> extends number ? NumberOperators<T, P> : PathValue$1<T, P> extends string ? StringOperators<T, P> : PathValue$1<T, P> extends boolean ? BooleanOperators<T, P> : PathValue$1<T, P> extends Date ? DateOperators<T, P> : PathValue$1<T, P> extends Array<infer U> ? ArrayOperators<T, P, U> : BaseOperators<T, P>;
|
|
107
|
+
type BaseOperators<T, P extends Path$1<T>> = {
|
|
108
|
+
eq: (value: PathValue$1<T, P>) => Condition$1<T>;
|
|
109
|
+
in: (values: PathValue$1<T, P>[]) => Condition$1<T>;
|
|
110
|
+
};
|
|
111
|
+
type NumberOperators<T, P extends Path$1<T>> = BaseOperators<T, P> & {
|
|
112
|
+
gt: (value: number) => Condition$1<T>;
|
|
113
|
+
gte: (value: number) => Condition$1<T>;
|
|
114
|
+
lt: (value: number) => Condition$1<T>;
|
|
115
|
+
lte: (value: number) => Condition$1<T>;
|
|
116
|
+
between: (min: number, max: number) => Condition$1<T>;
|
|
117
|
+
};
|
|
118
|
+
type StringOperators<T, P extends Path$1<T>> = BaseOperators<T, P> & {
|
|
119
|
+
contains: (value: string) => Condition$1<T>;
|
|
120
|
+
startsWith: (value: string) => Condition$1<T>;
|
|
121
|
+
matches: (value: RegExp) => Condition$1<T>;
|
|
122
|
+
};
|
|
123
|
+
type BooleanOperators<T, P extends Path$1<T>> = BaseOperators<T, P> & {
|
|
124
|
+
isTrue: () => Condition$1<T>;
|
|
125
|
+
isFalse: () => Condition$1<T>;
|
|
126
|
+
};
|
|
127
|
+
type DateOperators<T, P extends Path$1<T>> = BaseOperators<T, P> & {
|
|
128
|
+
before: (value: Date) => Condition$1<T>;
|
|
129
|
+
after: (value: Date) => Condition$1<T>;
|
|
130
|
+
};
|
|
131
|
+
type ArrayOperators<T, P extends Path$1<T>, U> = BaseOperators<T, P> & {
|
|
132
|
+
contains: (value: U) => Condition$1<T>;
|
|
133
|
+
any: (predicate: (item: U) => boolean, label?: string) => Condition$1<T>;
|
|
134
|
+
all: (predicate: (item: U) => boolean, label?: string) => Condition$1<T>;
|
|
135
|
+
};
|
|
136
|
+
type Field$1<T, P extends Path$1<T>> = {
|
|
137
|
+
path: P;
|
|
138
|
+
get: (facts: T) => PathValue$1<T, P>;
|
|
139
|
+
} & FieldOperators<T, P>;
|
|
140
|
+
/**
|
|
141
|
+
* Create a typed field accessor for a facts model.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```ts
|
|
145
|
+
* const factsField = Rules.field<Facts>();
|
|
146
|
+
* const isAdult = factsField("user.age").gte(18);
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
declare function field<T>(): <P extends Path$1<T>>(path: P) => Field$1<T, P>;
|
|
150
|
+
/**
|
|
151
|
+
* Create a typed field accessor for a specific path.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* const isVip = Rules.field<Facts>()("user.tags").contains("vip");
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
declare function field<T, P extends Path$1<T>>(path: P): Field$1<T, P>;
|
|
159
|
+
|
|
160
|
+
type ConditionDetails<Facts> = (facts: Facts) => Pick<ConditionTrace$1, "left" | "op" | "right">;
|
|
161
|
+
type ConditionOptions<Facts> = {
|
|
162
|
+
details?: ConditionDetails<Facts>;
|
|
163
|
+
reasonCode?: string;
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Create a condition with optional trace details and a reason code.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```ts
|
|
170
|
+
* const isAdult = Rules.condition("is adult", (facts: Facts) => facts.user.age >= 18, {
|
|
171
|
+
* details: (facts) => ({ left: facts.user.age, op: ">=", right: 18 }),
|
|
172
|
+
* reasonCode: "AGE_18",
|
|
173
|
+
* });
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
declare function condition<Facts>(label: string, test: (facts: Facts) => boolean, options?: ConditionDetails<Facts> | ConditionOptions<Facts>): Condition$1<Facts>;
|
|
177
|
+
|
|
178
|
+
type ConditionLabel = string | {
|
|
179
|
+
label: string;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Boolean and extensibility operators for composing conditions.
|
|
183
|
+
*/
|
|
184
|
+
declare const op: {
|
|
185
|
+
and: typeof and;
|
|
186
|
+
or: typeof or;
|
|
187
|
+
not: typeof not;
|
|
188
|
+
custom: typeof custom;
|
|
189
|
+
register: typeof register;
|
|
190
|
+
use: typeof use;
|
|
191
|
+
has: typeof has;
|
|
192
|
+
list: typeof list;
|
|
193
|
+
};
|
|
194
|
+
type OperatorFactory<Facts, Args extends unknown[]> = (...args: Args) => Condition$1<Facts>;
|
|
195
|
+
/**
|
|
196
|
+
* Combine conditions with logical AND.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```ts
|
|
200
|
+
* const combined = Rules.op.and(isAdult, isVip);
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
declare function and<Facts>(...conditions: Condition$1<Facts>[]): Condition$1<Facts>;
|
|
204
|
+
declare function and<Facts>(label: ConditionLabel, ...conditions: Condition$1<Facts>[]): Condition$1<Facts>;
|
|
205
|
+
/**
|
|
206
|
+
* Combine conditions with logical OR.
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```ts
|
|
210
|
+
* const combined = Rules.op.or(isAdult, isVip);
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
declare function or<Facts>(...conditions: Condition$1<Facts>[]): Condition$1<Facts>;
|
|
214
|
+
declare function or<Facts>(label: ConditionLabel, ...conditions: Condition$1<Facts>[]): Condition$1<Facts>;
|
|
215
|
+
/**
|
|
216
|
+
* Negate a condition.
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* ```ts
|
|
220
|
+
* const notVip = Rules.op.not(isVip);
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
declare function not<Facts>(conditionToNegate: Condition$1<Facts>): Condition$1<Facts>;
|
|
224
|
+
declare function not<Facts>(label: ConditionLabel, conditionToNegate: Condition$1<Facts>): Condition$1<Facts>;
|
|
225
|
+
/**
|
|
226
|
+
* Create a custom condition with an optional reason code and trace details.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```ts
|
|
230
|
+
* const isEven = Rules.op.custom("is-even", (facts: { value: number }) => facts.value % 2 === 0);
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
declare function custom<Facts>(label: string, test: (facts: Facts) => boolean, options?: ((facts: Facts) => Pick<ConditionTrace$1, "left" | "op" | "right">) | {
|
|
234
|
+
details?: (facts: Facts) => Pick<ConditionTrace$1, "left" | "op" | "right">;
|
|
235
|
+
reasonCode?: string;
|
|
236
|
+
}): Condition$1<Facts>;
|
|
237
|
+
/**
|
|
238
|
+
* Register a custom operator factory by name.
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```ts
|
|
242
|
+
* Rules.op.register("positive", () => Rules.condition("positive", (facts: { value: number }) => facts.value > 0));
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
declare function register<Facts, Args extends unknown[]>(name: string, factory: OperatorFactory<Facts, Args>): void;
|
|
246
|
+
/**
|
|
247
|
+
* Create a condition using a registered operator.
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```ts
|
|
251
|
+
* const positive = Rules.op.use<{ value: number }, []>("positive");
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
declare function use<Facts, Args extends unknown[]>(name: string, ...args: Args): Condition$1<Facts>;
|
|
255
|
+
/**
|
|
256
|
+
* Check if a custom operator name is registered.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```ts
|
|
260
|
+
* if (!Rules.op.has("positive")) { ... }
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
declare function has(name: string): boolean;
|
|
264
|
+
/**
|
|
265
|
+
* List registered operator names.
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```ts
|
|
269
|
+
* const names = Rules.op.list();
|
|
270
|
+
* ```
|
|
271
|
+
*/
|
|
272
|
+
declare function list(): string[];
|
|
273
|
+
|
|
274
|
+
type Rule<Facts, Effects> = {
|
|
275
|
+
id: string;
|
|
276
|
+
priority: number;
|
|
277
|
+
order: number;
|
|
278
|
+
conditions: Condition$1<Facts>[];
|
|
279
|
+
action: (ctx: ActionContext$1<Facts, Effects>) => AsyncActionResult<Effects>;
|
|
280
|
+
meta?: RuleMeta$1;
|
|
281
|
+
};
|
|
282
|
+
type DefaultEffectsFactory<Effects> = () => Effects;
|
|
283
|
+
type Engine<Facts, Effects> = {
|
|
284
|
+
run: (input: {
|
|
285
|
+
facts: Facts;
|
|
286
|
+
} & RunOptions$1) => RunResult$1<Effects>;
|
|
287
|
+
runAsync: (input: {
|
|
288
|
+
facts: Facts;
|
|
289
|
+
} & RunOptions$1) => Promise<RunResult$1<Effects>>;
|
|
290
|
+
};
|
|
291
|
+
type RulesetBuilderWithDefaults<Facts, Effects> = RulesetBuilderImpl<Facts, Effects, true>;
|
|
292
|
+
type RulesetBuilder<Facts, Effects, HasDefaults extends boolean> = Omit<RulesetBuilderImpl<Facts, Effects, HasDefaults>, "compile"> & (HasDefaults extends true ? {
|
|
293
|
+
compile(): Engine<Facts, Effects>;
|
|
294
|
+
} : {
|
|
295
|
+
compile: never;
|
|
296
|
+
});
|
|
297
|
+
type RuleBuilder<Facts, Effects, HasDefaults extends boolean, HasThen extends boolean> = Omit<RuleBuilderImpl<Facts, Effects, HasDefaults, HasThen>, "end"> & (HasThen extends true ? {
|
|
298
|
+
end(): RulesetBuilder<Facts, Effects, HasDefaults>;
|
|
299
|
+
} : {
|
|
300
|
+
end: never;
|
|
301
|
+
});
|
|
302
|
+
declare class RulesetBuilderImpl<Facts, Effects, HasDefaults extends boolean> {
|
|
303
|
+
private readonly name?;
|
|
304
|
+
private readonly rules;
|
|
305
|
+
private defaultEffectsFactory?;
|
|
306
|
+
private validateFactsFn?;
|
|
307
|
+
private validateEffectsFn?;
|
|
308
|
+
private nextOrder;
|
|
309
|
+
private registryId?;
|
|
310
|
+
private telemetryAdapter?;
|
|
311
|
+
constructor(name?: string);
|
|
312
|
+
/**
|
|
313
|
+
* Set the default effects factory. Required before compile().
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```ts
|
|
317
|
+
* const rs = Rules.ruleset<Facts, Effects>("rs")
|
|
318
|
+
* .defaultEffects(() => ({ flags: [] }));
|
|
319
|
+
* ```
|
|
320
|
+
*/
|
|
321
|
+
defaultEffects(factory: DefaultEffectsFactory<Effects>): RulesetBuilder<Facts, Effects, true>;
|
|
322
|
+
/**
|
|
323
|
+
* Provide a validation function for facts. Called before each run.
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* ```ts
|
|
327
|
+
* const rs = Rules.ruleset<Facts, Effects>("validate")
|
|
328
|
+
* .validateFacts((facts) => {
|
|
329
|
+
* if (!facts.user) throw new Error("missing user");
|
|
330
|
+
* });
|
|
331
|
+
* ```
|
|
332
|
+
*/
|
|
333
|
+
validateFacts(validator: (facts: Facts) => void): RulesetBuilder<Facts, Effects, HasDefaults>;
|
|
334
|
+
/**
|
|
335
|
+
* Provide a validation function for effects. Called after default effects creation
|
|
336
|
+
* and after each run completes.
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* ```ts
|
|
340
|
+
* const rs = Rules.ruleset<Facts, Effects>("validate")
|
|
341
|
+
* .validateEffects((effects) => {
|
|
342
|
+
* if (!Array.isArray(effects.flags)) throw new Error("invalid effects");
|
|
343
|
+
* });
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
346
|
+
validateEffects(validator: (effects: Effects) => void): RulesetBuilder<Facts, Effects, HasDefaults>;
|
|
347
|
+
/**
|
|
348
|
+
* Attach telemetry adapter (OpenTelemetry-compatible).
|
|
349
|
+
*
|
|
350
|
+
* @example
|
|
351
|
+
* ```ts
|
|
352
|
+
* const adapter = Rules.otel.createAdapter(trace.getTracer("rulit"));
|
|
353
|
+
* Rules.ruleset("rs").telemetry(adapter);
|
|
354
|
+
* ```
|
|
355
|
+
*/
|
|
356
|
+
telemetry(adapter: TelemetryAdapter$1): RulesetBuilder<Facts, Effects, HasDefaults>;
|
|
357
|
+
_setRegistryId(id: string): void;
|
|
358
|
+
/**
|
|
359
|
+
* Add a new rule to the ruleset.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```ts
|
|
363
|
+
* Rules.ruleset<Facts, Effects>("rs")
|
|
364
|
+
* .defaultEffects(() => ({ flags: [] }))
|
|
365
|
+
* .rule("vip")
|
|
366
|
+
* .when(factsField("user.tags").contains("vip"))
|
|
367
|
+
* .then(({ effects }) => effects.flags.push("vip"))
|
|
368
|
+
* .end();
|
|
369
|
+
* ```
|
|
370
|
+
*/
|
|
371
|
+
rule(id: string): RuleBuilder<Facts, Effects, HasDefaults, false>;
|
|
372
|
+
/**
|
|
373
|
+
* Create a typed field helper bound to the facts type.
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```ts
|
|
377
|
+
* const factsField = Rules.ruleset<Facts, Effects>("rs").field();
|
|
378
|
+
* const isAdult = factsField("user.age").gte(18);
|
|
379
|
+
* ```
|
|
380
|
+
*/
|
|
381
|
+
field(): <P extends Path$1<Facts>>(path: P) => Field$1<Facts, P>;
|
|
382
|
+
_addRule(rule: Rule<Facts, Effects>): void;
|
|
383
|
+
_nextOrder(): number;
|
|
384
|
+
/**
|
|
385
|
+
* Export a graph representation of the ruleset.
|
|
386
|
+
*
|
|
387
|
+
* @example
|
|
388
|
+
* ```ts
|
|
389
|
+
* const graph = ruleset.graph();
|
|
390
|
+
* ```
|
|
391
|
+
*/
|
|
392
|
+
graph(): RulesetGraph$1;
|
|
393
|
+
/**
|
|
394
|
+
* Export a Mermaid flowchart for visualization.
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* ```ts
|
|
398
|
+
* const mermaid = ruleset.toMermaid();
|
|
399
|
+
* ```
|
|
400
|
+
*/
|
|
401
|
+
toMermaid(): string;
|
|
402
|
+
/**
|
|
403
|
+
* Compile the ruleset into an engine.
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* ```ts
|
|
407
|
+
* const engine = Rules.ruleset<Facts, Effects>("rs")
|
|
408
|
+
* .defaultEffects(() => ({ flags: [] }))
|
|
409
|
+
* .compile();
|
|
410
|
+
* ```
|
|
411
|
+
*/
|
|
412
|
+
compile(this: RulesetBuilderWithDefaults<Facts, Effects>): Engine<Facts, Effects>;
|
|
413
|
+
}
|
|
414
|
+
declare class RuleBuilderImpl<Facts, Effects, HasDefaults extends boolean, HasThen extends boolean> {
|
|
415
|
+
private readonly ruleset;
|
|
416
|
+
private readonly id;
|
|
417
|
+
private priorityValue;
|
|
418
|
+
private conditions;
|
|
419
|
+
private action?;
|
|
420
|
+
private metaValue;
|
|
421
|
+
constructor(ruleset: RulesetBuilderImpl<Facts, Effects, HasDefaults>, id: string);
|
|
422
|
+
/**
|
|
423
|
+
* Set rule priority. Higher runs first.
|
|
424
|
+
*
|
|
425
|
+
* @example
|
|
426
|
+
* ```ts
|
|
427
|
+
* rule.priority(100);
|
|
428
|
+
* ```
|
|
429
|
+
*/
|
|
430
|
+
priority(value: number): RuleBuilder<Facts, Effects, HasDefaults, HasThen>;
|
|
431
|
+
/**
|
|
432
|
+
* Add conditions to a rule.
|
|
433
|
+
*
|
|
434
|
+
* @example
|
|
435
|
+
* ```ts
|
|
436
|
+
* rule.when(factsField("user.age").gte(18), factsField("user.tags").contains("vip"));
|
|
437
|
+
* ```
|
|
438
|
+
*/
|
|
439
|
+
when(...conditions: Condition$1<Facts>[]): RuleBuilder<Facts, Effects, HasDefaults, HasThen>;
|
|
440
|
+
/**
|
|
441
|
+
* Set rule metadata in one call.
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* ```ts
|
|
445
|
+
* rule.meta({ tags: ["vip"], version: "1.0.0", reasonCode: "VIP_RULE" });
|
|
446
|
+
* ```
|
|
447
|
+
*/
|
|
448
|
+
meta(meta: RuleMeta$1): RuleBuilder<Facts, Effects, HasDefaults, HasThen>;
|
|
449
|
+
/**
|
|
450
|
+
* Set rule tags used for filtering.
|
|
451
|
+
*
|
|
452
|
+
* @example
|
|
453
|
+
* ```ts
|
|
454
|
+
* rule.tags("vip", "adult");
|
|
455
|
+
* ```
|
|
456
|
+
*/
|
|
457
|
+
tags(...tags: string[]): RuleBuilder<Facts, Effects, HasDefaults, HasThen>;
|
|
458
|
+
/**
|
|
459
|
+
* Set a human-readable description.
|
|
460
|
+
*
|
|
461
|
+
* @example
|
|
462
|
+
* ```ts
|
|
463
|
+
* rule.description("VIP adult rule");
|
|
464
|
+
* ```
|
|
465
|
+
*/
|
|
466
|
+
description(description: string): RuleBuilder<Facts, Effects, HasDefaults, HasThen>;
|
|
467
|
+
/**
|
|
468
|
+
* Set a rule version string.
|
|
469
|
+
*
|
|
470
|
+
* @example
|
|
471
|
+
* ```ts
|
|
472
|
+
* rule.version("1.2.3");
|
|
473
|
+
* ```
|
|
474
|
+
*/
|
|
475
|
+
version(version: string): RuleBuilder<Facts, Effects, HasDefaults, HasThen>;
|
|
476
|
+
/**
|
|
477
|
+
* Set a reason code for audit/explain output.
|
|
478
|
+
*
|
|
479
|
+
* @example
|
|
480
|
+
* ```ts
|
|
481
|
+
* rule.reasonCode("VIP_ADULT");
|
|
482
|
+
* ```
|
|
483
|
+
*/
|
|
484
|
+
reasonCode(reasonCode: string): RuleBuilder<Facts, Effects, HasDefaults, HasThen>;
|
|
485
|
+
/**
|
|
486
|
+
* Enable or disable a rule.
|
|
487
|
+
*
|
|
488
|
+
* @example
|
|
489
|
+
* ```ts
|
|
490
|
+
* rule.enabled(false);
|
|
491
|
+
* ```
|
|
492
|
+
*/
|
|
493
|
+
enabled(enabled: boolean): RuleBuilder<Facts, Effects, HasDefaults, HasThen>;
|
|
494
|
+
/**
|
|
495
|
+
* Set the rule action. Returning a partial effects object applies a patch.
|
|
496
|
+
*
|
|
497
|
+
* @example
|
|
498
|
+
* ```ts
|
|
499
|
+
* rule.then(({ effects }) => {
|
|
500
|
+
* effects.flags.push("vip");
|
|
501
|
+
* });
|
|
502
|
+
* ```
|
|
503
|
+
*/
|
|
504
|
+
then(action: (ctx: ActionContext$1<Facts, Effects>) => ActionResult<Effects>): RuleBuilder<Facts, Effects, HasDefaults, true>;
|
|
505
|
+
/**
|
|
506
|
+
* Set an async rule action. Returning a partial effects object applies a patch.
|
|
507
|
+
*
|
|
508
|
+
* @example
|
|
509
|
+
* ```ts
|
|
510
|
+
* rule.thenAsync(async ({ effects }) => {
|
|
511
|
+
* effects.flags.push("vip");
|
|
512
|
+
* });
|
|
513
|
+
* ```
|
|
514
|
+
*/
|
|
515
|
+
thenAsync(action: (ctx: ActionContext$1<Facts, Effects>) => Promise<ActionResult<Effects>>): RuleBuilder<Facts, Effects, HasDefaults, true>;
|
|
516
|
+
/**
|
|
517
|
+
* Finalize the rule and return to the ruleset builder.
|
|
518
|
+
*
|
|
519
|
+
* @example
|
|
520
|
+
* ```ts
|
|
521
|
+
* ruleset.rule("vip").then(() => undefined).end();
|
|
522
|
+
* ```
|
|
523
|
+
*/
|
|
524
|
+
end(this: RuleBuilderImpl<Facts, Effects, HasDefaults, true>): RulesetBuilder<Facts, Effects, HasDefaults>;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Create a new ruleset builder.
|
|
528
|
+
*
|
|
529
|
+
* @example
|
|
530
|
+
* ```ts
|
|
531
|
+
* const rs = Rules.ruleset<Facts, Effects>("eligibility");
|
|
532
|
+
* ```
|
|
533
|
+
*/
|
|
534
|
+
declare function ruleset<Facts, Effects>(name?: string): RulesetBuilder<Facts, Effects, false>;
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Create a facts validator using a Zod schema.
|
|
538
|
+
*
|
|
539
|
+
* @example
|
|
540
|
+
* ```ts
|
|
541
|
+
* const factsSchema = z.object({ user: z.object({ age: z.number() }) });
|
|
542
|
+
* const rs = Rules.ruleset<Facts, Effects>("rs").validateFacts(Rules.zodFacts(factsSchema));
|
|
543
|
+
* ```
|
|
544
|
+
*/
|
|
545
|
+
declare function zodFacts<Facts>(schema: ZodType<Facts>): (facts: Facts) => void;
|
|
546
|
+
/**
|
|
547
|
+
* Create an effects validator using a Zod schema.
|
|
548
|
+
*
|
|
549
|
+
* @example
|
|
550
|
+
* ```ts
|
|
551
|
+
* const effectsSchema = z.object({ flags: z.array(z.string()) });
|
|
552
|
+
* const rs = Rules.ruleset<Facts, Effects>("rs").validateEffects(Rules.zodEffects(effectsSchema));
|
|
553
|
+
* ```
|
|
554
|
+
*/
|
|
555
|
+
declare function zodEffects<Effects>(schema: ZodType<Effects>): (effects: Effects) => void;
|
|
556
|
+
|
|
557
|
+
type GraphSource = {
|
|
558
|
+
graph: () => RulesetGraph$1;
|
|
559
|
+
toMermaid: () => string;
|
|
560
|
+
};
|
|
561
|
+
type RegistryListItem = {
|
|
562
|
+
id: string;
|
|
563
|
+
name?: string;
|
|
564
|
+
createdAt: number;
|
|
565
|
+
};
|
|
566
|
+
/**
|
|
567
|
+
* In-memory registry of rulesets created in this process.
|
|
568
|
+
*/
|
|
569
|
+
declare const registry: {
|
|
570
|
+
/**
|
|
571
|
+
* Register a ruleset in the global registry.
|
|
572
|
+
*
|
|
573
|
+
* @example
|
|
574
|
+
* ```ts
|
|
575
|
+
* const rs = Rules.ruleset<Facts, Effects>("eligibility");
|
|
576
|
+
* Rules.registry.list();
|
|
577
|
+
* ```
|
|
578
|
+
*/
|
|
579
|
+
register(source: GraphSource, name?: string): string;
|
|
580
|
+
/**
|
|
581
|
+
* List all registered rulesets.
|
|
582
|
+
*
|
|
583
|
+
* @example
|
|
584
|
+
* ```ts
|
|
585
|
+
* const list = Rules.registry.list();
|
|
586
|
+
* ```
|
|
587
|
+
*/
|
|
588
|
+
list(): RegistryListItem[];
|
|
589
|
+
/**
|
|
590
|
+
* Get a graph by id or ruleset name.
|
|
591
|
+
*
|
|
592
|
+
* @example
|
|
593
|
+
* ```ts
|
|
594
|
+
* const graph = Rules.registry.getGraph("eligibility");
|
|
595
|
+
* ```
|
|
596
|
+
*/
|
|
597
|
+
getGraph(idOrName: string): RulesetGraph$1 | undefined;
|
|
598
|
+
/**
|
|
599
|
+
* Get Mermaid output by id or ruleset name.
|
|
600
|
+
*
|
|
601
|
+
* @example
|
|
602
|
+
* ```ts
|
|
603
|
+
* const mermaid = Rules.registry.getMermaid("eligibility");
|
|
604
|
+
* ```
|
|
605
|
+
*/
|
|
606
|
+
getMermaid(idOrName: string): string | undefined;
|
|
607
|
+
/**
|
|
608
|
+
* Record a trace run for a ruleset. Keeps a rolling window of traces.
|
|
609
|
+
*
|
|
610
|
+
* @example
|
|
611
|
+
* ```ts
|
|
612
|
+
* Rules.registry.recordTrace("eligibility", trace, fired);
|
|
613
|
+
* ```
|
|
614
|
+
*/
|
|
615
|
+
recordTrace(idOrName: string, trace: RuleTrace$1[], fired: string[], facts: unknown): TraceRun$1 | undefined;
|
|
616
|
+
/**
|
|
617
|
+
* List trace runs for a ruleset.
|
|
618
|
+
*
|
|
619
|
+
* @example
|
|
620
|
+
* ```ts
|
|
621
|
+
* const traces = Rules.registry.listTraces("eligibility");
|
|
622
|
+
* ```
|
|
623
|
+
*/
|
|
624
|
+
listTraces(idOrName: string): TraceRun$1[];
|
|
625
|
+
/**
|
|
626
|
+
* Get a trace by id for a ruleset.
|
|
627
|
+
*
|
|
628
|
+
* @example
|
|
629
|
+
* ```ts
|
|
630
|
+
* const trace = Rules.registry.getTrace("eligibility", "trace-0");
|
|
631
|
+
* ```
|
|
632
|
+
*/
|
|
633
|
+
getTrace(idOrName: string, traceId: string): TraceRun$1 | undefined;
|
|
634
|
+
/**
|
|
635
|
+
* Clear the registry (useful in tests).
|
|
636
|
+
*
|
|
637
|
+
* @example
|
|
638
|
+
* ```ts
|
|
639
|
+
* Rules.registry.clear();
|
|
640
|
+
* ```
|
|
641
|
+
*/
|
|
642
|
+
clear(): void;
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
type OtelSpanLike = {
|
|
646
|
+
end: () => void;
|
|
647
|
+
recordException?: (error: unknown) => void;
|
|
648
|
+
setAttribute?: (key: string, value: unknown) => void;
|
|
649
|
+
setAttributes?: (attributes: Record<string, unknown>) => void;
|
|
650
|
+
};
|
|
651
|
+
type OtelTracerLike = {
|
|
652
|
+
startSpan: (name: string, options?: {
|
|
653
|
+
attributes?: Record<string, unknown>;
|
|
654
|
+
}) => OtelSpanLike;
|
|
655
|
+
};
|
|
656
|
+
/**
|
|
657
|
+
* Create a telemetry adapter from an OpenTelemetry tracer.
|
|
658
|
+
*
|
|
659
|
+
* @example
|
|
660
|
+
* ```ts
|
|
661
|
+
* import { trace } from "@opentelemetry/api";
|
|
662
|
+
* const adapter = Rules.otel.createAdapter(trace.getTracer("rulit"));
|
|
663
|
+
* Rules.ruleset("rs").telemetry(adapter);
|
|
664
|
+
* ```
|
|
665
|
+
*/
|
|
666
|
+
declare function createOtelAdapter(tracer: OtelTracerLike): TelemetryAdapter$1;
|
|
667
|
+
|
|
668
|
+
declare const Rules: {
|
|
669
|
+
ruleset: typeof ruleset;
|
|
670
|
+
condition: typeof condition;
|
|
671
|
+
field: typeof field;
|
|
672
|
+
op: {
|
|
673
|
+
and: {
|
|
674
|
+
<Facts>(...conditions: Condition$1<Facts>[]): Condition$1<Facts>;
|
|
675
|
+
<Facts>(label: string | {
|
|
676
|
+
label: string;
|
|
677
|
+
}, ...conditions: Condition$1<Facts>[]): Condition$1<Facts>;
|
|
678
|
+
};
|
|
679
|
+
or: {
|
|
680
|
+
<Facts>(...conditions: Condition$1<Facts>[]): Condition$1<Facts>;
|
|
681
|
+
<Facts>(label: string | {
|
|
682
|
+
label: string;
|
|
683
|
+
}, ...conditions: Condition$1<Facts>[]): Condition$1<Facts>;
|
|
684
|
+
};
|
|
685
|
+
not: {
|
|
686
|
+
<Facts>(conditionToNegate: Condition$1<Facts>): Condition$1<Facts>;
|
|
687
|
+
<Facts>(label: string | {
|
|
688
|
+
label: string;
|
|
689
|
+
}, conditionToNegate: Condition$1<Facts>): Condition$1<Facts>;
|
|
690
|
+
};
|
|
691
|
+
custom: <Facts>(label: string, test: (facts: Facts) => boolean, options?: ((facts: Facts) => Pick<ConditionTrace$1, "left" | "op" | "right">) | {
|
|
692
|
+
details?: (facts: Facts) => Pick<ConditionTrace$1, "left" | "op" | "right">;
|
|
693
|
+
reasonCode?: string;
|
|
694
|
+
}) => Condition$1<Facts>;
|
|
695
|
+
register: <Facts, Args extends unknown[]>(name: string, factory: (...args: Args) => Condition$1<Facts>) => void;
|
|
696
|
+
use: <Facts, Args extends unknown[]>(name: string, ...args: Args) => Condition$1<Facts>;
|
|
697
|
+
has: (name: string) => boolean;
|
|
698
|
+
list: () => string[];
|
|
699
|
+
};
|
|
700
|
+
registry: {
|
|
701
|
+
register(source: {
|
|
702
|
+
graph: () => RulesetGraph$1;
|
|
703
|
+
toMermaid: () => string;
|
|
704
|
+
}, name?: string): string;
|
|
705
|
+
list(): {
|
|
706
|
+
id: string;
|
|
707
|
+
name?: string;
|
|
708
|
+
createdAt: number;
|
|
709
|
+
}[];
|
|
710
|
+
getGraph(idOrName: string): RulesetGraph$1 | undefined;
|
|
711
|
+
getMermaid(idOrName: string): string | undefined;
|
|
712
|
+
recordTrace(idOrName: string, trace: RuleTrace$1[], fired: string[], facts: unknown): TraceRun$1 | undefined;
|
|
713
|
+
listTraces(idOrName: string): TraceRun$1[];
|
|
714
|
+
getTrace(idOrName: string, traceId: string): TraceRun$1 | undefined;
|
|
715
|
+
clear(): void;
|
|
716
|
+
};
|
|
717
|
+
zodFacts: typeof zodFacts;
|
|
718
|
+
zodEffects: typeof zodEffects;
|
|
719
|
+
otel: {
|
|
720
|
+
createAdapter: typeof createOtelAdapter;
|
|
721
|
+
};
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
type ActionContext<Facts, Effects> = ActionContext$1<Facts, Effects>;
|
|
725
|
+
type Condition<Facts> = Condition$1<Facts>;
|
|
726
|
+
type ConditionMeta<Facts> = ConditionMeta$1<Facts>;
|
|
727
|
+
type ConditionTrace = ConditionTrace$1;
|
|
728
|
+
type EffectsMode = EffectsMode$1;
|
|
729
|
+
type MergeStrategy = MergeStrategy$1;
|
|
730
|
+
type RuleActivation = RuleActivation$1;
|
|
731
|
+
type RuleMeta = RuleMeta$1;
|
|
732
|
+
type RulesetGraph = RulesetGraph$1;
|
|
733
|
+
type GraphNode = GraphNode$1;
|
|
734
|
+
type GraphEdge = GraphEdge$1;
|
|
735
|
+
type RuleTrace = RuleTrace$1;
|
|
736
|
+
type RunOptions = RunOptions$1;
|
|
737
|
+
type RunResult<Effects> = RunResult$1<Effects>;
|
|
738
|
+
type TraceRun = TraceRun$1;
|
|
739
|
+
type TelemetryAdapter = TelemetryAdapter$1;
|
|
740
|
+
type TelemetryAttributes = TelemetryAttributes$1;
|
|
741
|
+
type Field<T, P extends Path$1<T>> = Field$1<T, P>;
|
|
742
|
+
type Path<T> = Path$1<T>;
|
|
743
|
+
type PathValue<T, P extends Path$1<T>> = PathValue$1<T, P>;
|
|
744
|
+
|
|
745
|
+
export { type ActionContext, type Condition, type ConditionMeta, type ConditionTrace, type EffectsMode, type Field, type GraphEdge, type GraphNode, type MergeStrategy, type Path, type PathValue, type RuleActivation, type RuleMeta, type RuleTrace, Rules, type RulesetGraph, type RunOptions, type RunResult, type TelemetryAdapter, type TelemetryAttributes, type TraceRun, condition, createOtelAdapter, field, op, registry, ruleset, zodEffects, zodFacts };
|