@opencxh/domain 1.266.0 → 1.274.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/dist/entities/rule/condition-flat.test.d.ts +1 -0
- package/dist/entities/rule/condition.d.ts +52 -0
- package/dist/entities/rule/condition.test.d.ts +1 -0
- package/dist/entities/rule/index.d.ts +2 -0
- package/dist/entities/rule/trigger-source.d.ts +137 -0
- package/dist/entities/rule/trigger-source.test.d.ts +1 -0
- package/dist/entities/rule/types.d.ts +2 -0
- package/dist/index.cjs +20 -20
- package/dist/index.js +1521 -1433
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONLogic as a tree a person can edit, and back again.
|
|
3
|
+
*
|
|
4
|
+
* In `domain` and not in an app because there are now two builders on the same language: the
|
|
5
|
+
* playbook's condition field and the rule editor. Two parsers for one expression language is two
|
|
6
|
+
* places for "what does `!` on a variable mean" to be answered differently — and a condition that
|
|
7
|
+
* round-trips through one and not the other silently changes what a rule does.
|
|
8
|
+
*
|
|
9
|
+
* Pure, no dependencies: the evaluator itself (json-logic-js) lives with the executor.
|
|
10
|
+
*/
|
|
11
|
+
export type ComparisonOp = "==" | "!=" | ">" | "<" | ">=" | "<=";
|
|
12
|
+
/**
|
|
13
|
+
* Truth tests with no right-hand side. Needed because an `extract` step yields `null` for a
|
|
14
|
+
* field that was not in the message: "is not equal to empty string" is then true, and a gate
|
|
15
|
+
* that trusts that lets exactly the empty cases through. There is no literal you can put
|
|
16
|
+
* beside `==` to catch that, so this is its own operator.
|
|
17
|
+
*/
|
|
18
|
+
export type UnaryOp = "filled" | "empty";
|
|
19
|
+
export type Op = ComparisonOp | "in" | UnaryOp;
|
|
20
|
+
export type Literal = string | number | boolean;
|
|
21
|
+
export declare const UNARY_OPS: ReadonlySet<Op>;
|
|
22
|
+
export interface GroupNode {
|
|
23
|
+
kind: "group";
|
|
24
|
+
op: "and" | "or";
|
|
25
|
+
children: BuilderNode[];
|
|
26
|
+
}
|
|
27
|
+
export interface ConditionLeaf {
|
|
28
|
+
kind: "rule";
|
|
29
|
+
op: Op;
|
|
30
|
+
varPath: string;
|
|
31
|
+
value: Literal;
|
|
32
|
+
}
|
|
33
|
+
export interface NotNode {
|
|
34
|
+
kind: "not";
|
|
35
|
+
child: BuilderNode;
|
|
36
|
+
}
|
|
37
|
+
export type BuilderNode = GroupNode | ConditionLeaf | NotNode;
|
|
38
|
+
export declare function emptyConditionLeaf(): ConditionLeaf;
|
|
39
|
+
/** Parse a JSONLogic expression into the BuilderNode tree; null if unmodelable (raw fallback). */
|
|
40
|
+
export declare function parse(expr: unknown): GroupNode | null;
|
|
41
|
+
/** Serialize a BuilderNode tree back to JSONLogic (inverse of parse). */
|
|
42
|
+
export declare function serialize(node: BuilderNode): unknown;
|
|
43
|
+
/**
|
|
44
|
+
* The rule as one readable expression, for the summary on a step card.
|
|
45
|
+
* `null` if the expression cannot be modelled (then the card shows nothing
|
|
46
|
+
* instead of a half-truth).
|
|
47
|
+
*
|
|
48
|
+
* Deliberately in **operators**, not words: `$a >= 0.7` is the same in every
|
|
49
|
+
* language, so this rule need not go through i18n — and `OP_LABELS` (which is
|
|
50
|
+
* Dutch) does not belong on a card everyone reads.
|
|
51
|
+
*/
|
|
52
|
+
export declare function describeExpression(expr: unknown): string | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { ToolCatalogEntry } from '../../platform/ai-tools';
|
|
2
|
+
/**
|
|
3
|
+
* What kind of value sits at a field, so the builder knows which widget to draw and the
|
|
4
|
+
* comparison operators that make sense.
|
|
5
|
+
*
|
|
6
|
+
* No `object`: a rule compares a leaf. Offering a whole object as the left-hand side of `==`
|
|
7
|
+
* produces a condition that is never true and reads as if it should be.
|
|
8
|
+
*
|
|
9
|
+
* `string[]` is here for the same reason rather than being folded into `string`. Tags, labels and
|
|
10
|
+
* party keys are the most obvious things to write a rule about, and on an array only `in` means
|
|
11
|
+
* anything — `==` against one is the silent never-matches this whole seam exists to prevent.
|
|
12
|
+
*/
|
|
13
|
+
export type EventFieldType = "string" | "string[]" | "number" | "boolean" | "date";
|
|
14
|
+
/** Operators that mean something for this kind of value. The builder offers no others. */
|
|
15
|
+
export declare function opsForFieldType(type: EventFieldType): readonly string[];
|
|
16
|
+
/** One choice for a field with a fixed or looked-up set of values. */
|
|
17
|
+
export interface EventFieldOption {
|
|
18
|
+
value: string;
|
|
19
|
+
/**
|
|
20
|
+
* The label as the server could build it. For something an admin typed (a project status, an
|
|
21
|
+
* inbox) that is the text itself; for one of ours it is the already-resolved fallback.
|
|
22
|
+
*/
|
|
23
|
+
label: string;
|
|
24
|
+
/**
|
|
25
|
+
* Translation key, for a value that is *ours* and therefore translatable.
|
|
26
|
+
*
|
|
27
|
+
* Same split as `ActivityPreview.snippet`/`snippetKey`, and for the same reason: the list is
|
|
28
|
+
* built once on a server that has no user language, while every reader has their own. The
|
|
29
|
+
* builder prefers this and falls back to `label`.
|
|
30
|
+
*/
|
|
31
|
+
labelKey?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* One thing a rule can test, declared by the app that writes the row.
|
|
35
|
+
*
|
|
36
|
+
* **Why declared and not listed centrally.** A hand-written table of fields drifts from the
|
|
37
|
+
* payload it describes, and the drift is silent: a rule on a path that yields `undefined` never
|
|
38
|
+
* fires and says nothing about why. That already happened here once — `TRIGGER_VARS` exists
|
|
39
|
+
* because the picker offered `trigger.fromEmail` and `trigger.channel`, neither of which the
|
|
40
|
+
* engine ever filled. A central list for comms, work and sales would be that same table with
|
|
41
|
+
* three apps to drift from instead of one.
|
|
42
|
+
*/
|
|
43
|
+
export interface EventField {
|
|
44
|
+
/**
|
|
45
|
+
* Path under `subject`, dot-notated: `"priority"`, `"lastActivityPreview.text"`.
|
|
46
|
+
*
|
|
47
|
+
* Relative, because `subject` is where the owning app's row lands in the condition context
|
|
48
|
+
* (`matchRules` builds `{ event, subject }`) and repeating that prefix in every declaration is
|
|
49
|
+
* an invitation to get it wrong in one of them. {@link fieldVarPath} adds it, once.
|
|
50
|
+
*/
|
|
51
|
+
path: string;
|
|
52
|
+
type: EventFieldType;
|
|
53
|
+
/** What a person calls this. The builder shows this; the path is never on screen. */
|
|
54
|
+
label: string;
|
|
55
|
+
/**
|
|
56
|
+
* A fixed set of values — the builder offers a dropdown instead of a free text field.
|
|
57
|
+
*
|
|
58
|
+
* For what the *app* defines (a priority, a direction, a status category). What the
|
|
59
|
+
* *organisation* defines goes through {@link lookup}, because it is data and not vocabulary.
|
|
60
|
+
*/
|
|
61
|
+
values?: EventFieldOption[];
|
|
62
|
+
/**
|
|
63
|
+
* The values are organisation data: ask the declaring app via `/provider/triggers/options`.
|
|
64
|
+
*
|
|
65
|
+
* This exists so the builder needs no per-field knowledge. Today `ConditionBuilder` has a
|
|
66
|
+
* `topicOptions()` that recognises a topic *by field name* — one hard-coded exception, and
|
|
67
|
+
* the second one (inboxes, users, statuses, pipelines) makes it a pattern.
|
|
68
|
+
*/
|
|
69
|
+
lookup?: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* One event an app publishes, and what a rule can ask about its subject.
|
|
73
|
+
*
|
|
74
|
+
* `fields` is per event and not per app on purpose: the subject differs even within one app —
|
|
75
|
+
* comms hands over the interaction, work the item, sales the transaction.
|
|
76
|
+
*/
|
|
77
|
+
export interface EventDescriptor {
|
|
78
|
+
/** As the bus delivers it, app-prefixed: `"communication:activities:created"`. */
|
|
79
|
+
name: string;
|
|
80
|
+
/** What this event is, in the author's language: "Er komt een bericht binnen". */
|
|
81
|
+
label: string;
|
|
82
|
+
/**
|
|
83
|
+
* What the subject is. Also the key the undo reader uses to fetch current state, so a new
|
|
84
|
+
* subject kind needs a reader there before its runs can be undone.
|
|
85
|
+
*/
|
|
86
|
+
subjectKind: string;
|
|
87
|
+
fields: EventField[];
|
|
88
|
+
}
|
|
89
|
+
/** What a `trigger-source` returns on `GET /provider/triggers/describe`. */
|
|
90
|
+
export interface TriggerSourceDescription {
|
|
91
|
+
events: EventDescriptor[];
|
|
92
|
+
}
|
|
93
|
+
/** An event plus the app that answered for it — the merged catalog a builder reads. */
|
|
94
|
+
export interface VocabularyEvent extends EventDescriptor {
|
|
95
|
+
/**
|
|
96
|
+
* From the runtime, never from a field inside the payload: that would let one app claim
|
|
97
|
+
* another's events. Same rule the rest of the fan-out helpers hold.
|
|
98
|
+
*/
|
|
99
|
+
app: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Everything a builder needs to write a rule: what it can react to, and what it can do.
|
|
103
|
+
*
|
|
104
|
+
* Both halves in one answer because they are one question — "what is possible here" — and because
|
|
105
|
+
* they are read together on every builder render. The action half is the tool federation with the
|
|
106
|
+
* model taken out from in front; it is filtered to what a rule may actually use, so the builder
|
|
107
|
+
* never has to show something it would refuse to save.
|
|
108
|
+
*/
|
|
109
|
+
export interface RuleVocabulary {
|
|
110
|
+
events: VocabularyEvent[];
|
|
111
|
+
/** The actions a rule may use. Outward ones are absent, not disabled — see `validateActions`. */
|
|
112
|
+
actions: ToolCatalogEntry[];
|
|
113
|
+
/** Apps that declare the role but answered nothing. Silence is named, not hidden. */
|
|
114
|
+
degradedApps: string[];
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The JSONLogic variable for a field: `subject.<path>`.
|
|
118
|
+
*
|
|
119
|
+
* One function so the builder, the templates and the declaration tests cannot disagree about the
|
|
120
|
+
* prefix. `matchRules` evaluates against `{ event, subject }`, and a condition written against
|
|
121
|
+
* the bare path silently compares `undefined`.
|
|
122
|
+
*/
|
|
123
|
+
export declare function fieldVarPath(field: Pick<EventField, "path">): string;
|
|
124
|
+
/** The event with this name, or `undefined`. */
|
|
125
|
+
export declare function findEvent(vocabulary: Pick<RuleVocabulary, "events">, name: string): VocabularyEvent | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* Which declared paths the real subject does not have.
|
|
128
|
+
*
|
|
129
|
+
* **This is the point of declaring fields near the code that writes them.** A declaration is only
|
|
130
|
+
* worth more than a central list if something holds it against reality, and this is that
|
|
131
|
+
* something: each app's test feeds a real row through and asserts the answer is empty.
|
|
132
|
+
*
|
|
133
|
+
* A path is "missing" when it resolves to `undefined` — absent, or running through a `null`.
|
|
134
|
+
* `null` itself counts as present: a cleared assignee is a value a rule can meaningfully test
|
|
135
|
+
* for, and treating it as missing would make every declaration fail on an empty row.
|
|
136
|
+
*/
|
|
137
|
+
export declare function missingFieldPaths(fields: readonly EventField[], subject: unknown): string[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -60,6 +60,8 @@ export interface Rule {
|
|
|
60
60
|
organizationId: string;
|
|
61
61
|
ownerScope: OwnerScope;
|
|
62
62
|
name: string;
|
|
63
|
+
/** What this rule is for, in the author's own words. */
|
|
64
|
+
description?: string;
|
|
63
65
|
enabled: boolean;
|
|
64
66
|
/**
|
|
65
67
|
* Only `event` runs today. The other arms exist because the vocabulary is shared; a rule on a
|