@rytass/bpm-core-nestjs-module 0.19.0 → 0.20.1
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/CHANGELOG.md +55 -0
- package/package.json +3 -2
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/index.js.map +1 -1
- package/src/lib/bpm/bpm-root-options.d.ts +13 -0
- package/src/lib/bpm/bpm-root-options.js.map +1 -1
- package/src/lib/bpm/bpm-root.module.d.ts +12 -0
- package/src/lib/bpm/bpm-root.module.js +5 -0
- package/src/lib/bpm/bpm-root.module.js.map +1 -1
- package/src/lib/condition/adapters/cel-legacy.adapter.d.ts +62 -0
- package/src/lib/condition/adapters/cel-legacy.adapter.js +141 -0
- package/src/lib/condition/adapters/cel-legacy.adapter.js.map +1 -0
- package/src/lib/condition/adapters/cel-v2.adapter.d.ts +76 -0
- package/src/lib/condition/adapters/cel-v2.adapter.js +371 -0
- package/src/lib/condition/adapters/cel-v2.adapter.js.map +1 -0
- package/src/lib/condition/condition.service.d.ts +90 -5
- package/src/lib/condition/condition.service.js +287 -71
- package/src/lib/condition/condition.service.js.map +1 -1
- package/src/lib/condition/expression-ast.d.ts +176 -0
- package/src/lib/condition/expression-ast.js +1254 -0
- package/src/lib/condition/expression-ast.js.map +1 -0
- package/src/lib/condition/expression-budget.d.ts +44 -0
- package/src/lib/condition/expression-budget.js +61 -0
- package/src/lib/condition/expression-budget.js.map +1 -0
- package/src/lib/condition/expression-dialect.d.ts +29 -0
- package/src/lib/condition/expression-dialect.js +44 -0
- package/src/lib/condition/expression-dialect.js.map +1 -0
- package/src/lib/condition/expression-differential.corpus.d.ts +34 -0
- package/src/lib/condition/expression-differential.corpus.js +995 -0
- package/src/lib/condition/expression-differential.corpus.js.map +1 -0
- package/src/lib/condition/expression-differential.d.ts +33 -0
- package/src/lib/condition/expression-differential.js +48 -0
- package/src/lib/condition/expression-differential.js.map +1 -0
- package/src/lib/condition/expression-engine-failure.d.ts +15 -0
- package/src/lib/condition/expression-engine-failure.js +22 -0
- package/src/lib/condition/expression-engine-failure.js.map +1 -0
- package/src/lib/condition/expression-errors.d.ts +40 -0
- package/src/lib/condition/expression-errors.js +121 -0
- package/src/lib/condition/expression-errors.js.map +1 -0
- package/src/lib/condition/expression-function-invocation.d.ts +50 -0
- package/src/lib/condition/expression-function-invocation.js +290 -0
- package/src/lib/condition/expression-function-invocation.js.map +1 -0
- package/src/lib/condition/expression-result.walker.d.ts +18 -0
- package/src/lib/condition/expression-result.walker.js +152 -0
- package/src/lib/condition/expression-result.walker.js.map +1 -0
- package/src/lib/condition/expression-runtime.d.ts +70 -0
- package/src/lib/condition/expression-runtime.js +134 -0
- package/src/lib/condition/expression-runtime.js.map +1 -0
- package/src/lib/condition/index.d.ts +4 -0
- package/src/lib/condition/index.js +8 -0
- package/src/lib/condition/index.js.map +1 -1
- package/src/lib/delegation/delegation-rule.entity.d.ts +7 -0
- package/src/lib/delegation/delegation-rule.entity.js +10 -0
- package/src/lib/delegation/delegation-rule.entity.js.map +1 -1
- package/src/lib/delegation/delegation.service.d.ts +7 -1
- package/src/lib/delegation/delegation.service.js +22 -5
- package/src/lib/delegation/delegation.service.js.map +1 -1
- package/src/lib/delegation/dto/delegation-rule.input.d.ts +2 -0
- package/src/lib/delegation/dto/delegation-rule.input.js +10 -0
- package/src/lib/delegation/dto/delegation-rule.input.js.map +1 -1
- package/src/lib/expression-function/expression-function.built-ins.d.ts +9 -0
- package/src/lib/expression-function/expression-function.built-ins.js +117 -0
- package/src/lib/expression-function/expression-function.built-ins.js.map +1 -0
- package/src/lib/expression-function/expression-function.module.d.ts +19 -0
- package/src/lib/expression-function/expression-function.module.js +34 -0
- package/src/lib/expression-function/expression-function.module.js.map +1 -0
- package/src/lib/expression-function/expression-function.provider.d.ts +17 -0
- package/src/lib/expression-function/expression-function.provider.js +39 -0
- package/src/lib/expression-function/expression-function.provider.js.map +1 -0
- package/src/lib/expression-function/expression-function.registry.d.ts +36 -0
- package/src/lib/expression-function/expression-function.registry.js +231 -0
- package/src/lib/expression-function/expression-function.registry.js.map +1 -0
- package/src/lib/expression-function/expression-function.types.d.ts +30 -0
- package/src/lib/expression-function/expression-function.types.js +8 -0
- package/src/lib/expression-function/expression-function.types.js.map +1 -0
- package/src/lib/expression-function/index.d.ts +5 -0
- package/src/lib/expression-function/index.js +9 -0
- package/src/lib/expression-function/index.js.map +1 -0
- package/src/lib/form/form-schema.validator.js +53 -6
- package/src/lib/form/form-schema.validator.js.map +1 -1
- package/src/lib/form-data-source/form-data-source-value-resolver.service.d.ts +12 -1
- package/src/lib/form-data-source/form-data-source-value-resolver.service.js +45 -26
- package/src/lib/form-data-source/form-data-source-value-resolver.service.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.expression.d.ts +95 -0
- package/src/lib/form-data-source/form-data-source.expression.js +267 -0
- package/src/lib/form-data-source/form-data-source.expression.js.map +1 -0
- package/src/lib/form-data-source/form-data-source.module.js +4 -0
- package/src/lib/form-data-source/form-data-source.module.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.service.d.ts +22 -3
- package/src/lib/form-data-source/form-data-source.service.js +78 -18
- package/src/lib/form-data-source/form-data-source.service.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.validation.d.ts +47 -4
- package/src/lib/form-data-source/form-data-source.validation.js +151 -23
- package/src/lib/form-data-source/form-data-source.validation.js.map +1 -1
- package/src/lib/migrations/0000000030000-delegation-scope-condition-dialect.d.ts +22 -0
- package/src/lib/migrations/0000000030000-delegation-scope-condition-dialect.js +76 -0
- package/src/lib/migrations/0000000030000-delegation-scope-condition-dialect.js.map +1 -0
- package/src/lib/migrations/index.d.ts +1 -0
- package/src/lib/migrations/index.js +3 -0
- package/src/lib/migrations/index.js.map +1 -1
- package/src/lib/notification/notification.service.d.ts +1 -1
- package/src/lib/notification/notification.service.js +7 -3
- package/src/lib/notification/notification.service.js.map +1 -1
- package/src/lib/template/dto/expression-designer.dto.d.ts +107 -0
- package/src/lib/template/dto/expression-designer.dto.js +415 -0
- package/src/lib/template/dto/expression-designer.dto.js.map +1 -0
- package/src/lib/template/expression-designer.queries.d.ts +16 -0
- package/src/lib/template/expression-designer.queries.js +66 -0
- package/src/lib/template/expression-designer.queries.js.map +1 -0
- package/src/lib/template/expression-designer.service.d.ts +72 -0
- package/src/lib/template/expression-designer.service.js +543 -0
- package/src/lib/template/expression-designer.service.js.map +1 -0
- package/src/lib/template/index.d.ts +2 -0
- package/src/lib/template/index.js +2 -0
- package/src/lib/template/index.js.map +1 -1
- package/src/lib/template/template.module.js +4 -0
- package/src/lib/template/template.module.js.map +1 -1
- package/src/lib/template/template.service.d.ts +37 -1
- package/src/lib/template/template.service.js +317 -29
- package/src/lib/template/template.service.js.map +1 -1
- package/src/lib/template/workflow-definition.validator.d.ts +7 -0
- package/src/lib/template/workflow-definition.validator.js +26 -2
- package/src/lib/template/workflow-definition.validator.js.map +1 -1
- package/src/lib/workflow-engine/time-budget.d.ts +13 -0
- package/src/lib/workflow-engine/time-budget.js +58 -0
- package/src/lib/workflow-engine/time-budget.js.map +1 -0
- package/src/lib/workflow-engine/workflow-condition-evaluator.js +12 -12
- package/src/lib/workflow-engine/workflow-condition-evaluator.js.map +1 -1
- package/src/lib/workflow-engine/workflow-engine.service.d.ts +24 -0
- package/src/lib/workflow-engine/workflow-engine.service.js +311 -88
- package/src/lib/workflow-engine/workflow-engine.service.js.map +1 -1
- package/src/lib/workflow-engine/workflow-form-value.d.ts +11 -0
- package/src/lib/workflow-engine/workflow-form-value.js +33 -0
- package/src/lib/workflow-engine/workflow-form-value.js.map +1 -0
- package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.js +4 -0
- package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.js.map +1 -1
- package/src/lib/workflow-webhook/workflow-webhook-delivery.service.d.ts +3 -1
- package/src/lib/workflow-webhook/workflow-webhook-delivery.service.js +11 -2
- package/src/lib/workflow-webhook/workflow-webhook-delivery.service.js.map +1 -1
- package/src/lib/workflow-webhook/workflow-webhook-enqueue.d.ts +8 -1
- package/src/lib/workflow-webhook/workflow-webhook-enqueue.js +70 -27
- package/src/lib/workflow-webhook/workflow-webhook-enqueue.js.map +1 -1
- package/src/lib/workflow-webhook/workflow-webhook-expression.d.ts +57 -0
- package/src/lib/workflow-webhook/workflow-webhook-expression.js +123 -0
- package/src/lib/workflow-webhook/workflow-webhook-expression.js.map +1 -0
- package/src/lib/workflow-webhook/workflow-webhook.module.js +4 -0
- package/src/lib/workflow-webhook/workflow-webhook.module.js.map +1 -1
- package/src/lib/workflow-webhook/workflow-webhook.validator.d.ts +7 -0
- package/src/lib/workflow-webhook/workflow-webhook.validator.js +29 -6
- package/src/lib/workflow-webhook/workflow-webhook.validator.js.map +1 -1
|
@@ -1,62 +1,181 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var ConditionService_1;
|
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
4
|
exports.ConditionService = void 0;
|
|
4
5
|
const tslib_1 = require("tslib");
|
|
5
6
|
const common_1 = require("@nestjs/common");
|
|
6
|
-
|
|
7
|
+
const bpm_core_shared_1 = require("@rytass/bpm-core-shared");
|
|
8
|
+
const expression_errors_1 = require("./expression-errors");
|
|
9
|
+
const expression_budget_1 = require("./expression-budget");
|
|
10
|
+
const expression_engine_failure_1 = require("./expression-engine-failure");
|
|
11
|
+
const expression_runtime_1 = require("./expression-runtime");
|
|
12
|
+
const cel_legacy_adapter_1 = require("./adapters/cel-legacy.adapter");
|
|
13
|
+
const cel_v2_adapter_1 = require("./adapters/cel-v2.adapter");
|
|
14
|
+
const expression_function_registry_1 = require("../expression-function/expression-function.registry");
|
|
15
|
+
const expression_function_types_1 = require("../expression-function/expression-function.types");
|
|
16
|
+
const expression_dialect_1 = require("./expression-dialect");
|
|
17
|
+
const expression_ast_1 = require("./expression-ast");
|
|
7
18
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* file, the package root pulled it in through
|
|
13
|
-
* `index → bpm-root → delegation → condition`, so *any* consumer test that
|
|
14
|
-
* imported anything from `@rytass/bpm-core-nestjs-module` failed to load with
|
|
15
|
-
* `Cannot find module 'chevrotain'` — pointing deep into `node_modules`, far
|
|
16
|
-
* from the code under test. Hosts had to stub `cel-js` before they could write
|
|
17
|
-
* a single test.
|
|
18
|
-
*
|
|
19
|
-
* Deferring the load means a host that never evaluates a CEL expression never
|
|
20
|
-
* resolves the package at all. `require` (not `await import`) keeps every
|
|
21
|
-
* caller in this file synchronous.
|
|
19
|
+
* The warm-up expression: a root read, a comparison and arithmetic, so the
|
|
20
|
+
* engine's parser, interpreter, member access and the budget's `vm` timeout
|
|
21
|
+
* all run once. It calls no function, so a host registry is only read (to
|
|
22
|
+
* build the environment), never invoked.
|
|
22
23
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
const WARM_UP_EXPRESSION = 'form.warmUp + 1 == 2';
|
|
25
|
+
const WARM_UP_CONTEXT = { form: { warmUp: 1 } };
|
|
26
|
+
const WARM_UP_LABEL = 'warmUp';
|
|
27
|
+
// Generous on purpose: the cold first evaluation is exactly what is slow
|
|
28
|
+
// (Phase 23 P3 measured about 90 ms against the 100 ms budget).
|
|
29
|
+
const WARM_UP_BUDGET = { cumulativeMs: 5_000, perExpressionMs: 5_000 };
|
|
30
|
+
const WARM_UP_DIALECTS = ['cel-legacy', 'cel-v2'];
|
|
31
|
+
/** `code=EXPRESSION_WARM_UP_DONE cel-legacy=1.2ms cel-v2=3.4ms`. */
|
|
32
|
+
const formatWarmUpLine = (result) => [
|
|
33
|
+
'code=EXPRESSION_WARM_UP_DONE',
|
|
34
|
+
...WARM_UP_DIALECTS.map((dialect) => {
|
|
35
|
+
const elapsed = result[dialect];
|
|
36
|
+
return `${dialect}=${elapsed === null ? 'failed' : `${elapsed.toFixed(1)}ms`}`;
|
|
37
|
+
}),
|
|
38
|
+
].join(' ');
|
|
39
|
+
const DEFAULT_MAX_EXPRESSION_LENGTH = 2000;
|
|
40
|
+
// `has` and `size` are CEL macros, not context identifiers; the root-identifier
|
|
41
|
+
// lint must not report them as unknown. `size` is required by the table field
|
|
42
|
+
// IS_FILLED / IS_EMPTY expressions (ADR 16 §3.8).
|
|
43
|
+
const CEL_KEYWORDS = new Set(['false', 'has', 'in', 'null', 'size', 'true']);
|
|
44
|
+
let ConditionService = ConditionService_1 = class ConditionService {
|
|
45
|
+
/**
|
|
46
|
+
* Evaluates and lints with the expression function registry
|
|
47
|
+
* (`BPM_EXPRESSION_FUNCTION_REGISTRY`, ADR 22 §3.5): both dialects call its
|
|
48
|
+
* functions and the cel-v2 lint checks against it. The injection is
|
|
49
|
+
* optional and `ConditionService` is also built with `new` in hosts and
|
|
50
|
+
* tests, so `new ConditionService()` still works: only the nine built-ins.
|
|
51
|
+
*/
|
|
52
|
+
constructor(functionRegistry) {
|
|
53
|
+
this.logger = new common_1.Logger(ConditionService_1.name);
|
|
54
|
+
this.functionRegistry =
|
|
55
|
+
functionRegistry ?? expression_function_registry_1.BUILT_IN_EXPRESSION_FUNCTION_REGISTRY;
|
|
56
|
+
// The built-ins-only registry gets the shared default adapters.
|
|
57
|
+
this.runtime = expression_runtime_1.ExpressionRuntime.withFunctions(this.functionRegistry);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Warms both engines at boot so the first case does not pay for it (Phase 23
|
|
61
|
+
* P3 live finding: the first evaluation after a restart took about 90 ms of
|
|
62
|
+
* its 100 ms budget, against about 1 ms warm).
|
|
63
|
+
*
|
|
64
|
+
* A warm-up that succeeded for any dialect writes one `log` line with each
|
|
65
|
+
* dialect's time (a failed one reads `failed` and has already warned with
|
|
66
|
+
* `EXPRESSION_WARM_UP_FAILED`), so a deployment can see it ran. The line
|
|
67
|
+
* holds only fixed words and milliseconds: no expression, no data. It is
|
|
68
|
+
* written here, not in `warmUp()`, so calling `warmUp()` stays silent.
|
|
69
|
+
*/
|
|
70
|
+
onApplicationBootstrap() {
|
|
71
|
+
const result = this.warmUp();
|
|
72
|
+
if (WARM_UP_DIALECTS.some((dialect) => result[dialect] !== null)) {
|
|
73
|
+
this.logger.log(formatWarmUpLine(result));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Loads each dialect's engine and this registry's environment, then parses,
|
|
78
|
+
* lints and evaluates one expression per dialect under a generous budget of
|
|
79
|
+
* its own, so module loading, the cel-v2 environment clone and the `vm`
|
|
80
|
+
* context are paid here. Never throws: a dialect that fails (a host
|
|
81
|
+
* registry that cannot be read, say) is logged with a fixed code and left
|
|
82
|
+
* cold, and the evaluations it serves fail closed as before.
|
|
83
|
+
*/
|
|
84
|
+
warmUp() {
|
|
85
|
+
const entries = WARM_UP_DIALECTS.map((dialect) => {
|
|
86
|
+
const startedAt = performance.now();
|
|
87
|
+
try {
|
|
88
|
+
this.prepareEngine(dialect);
|
|
89
|
+
this.lintExpressionIssues(WARM_UP_EXPRESSION, WARM_UP_LABEL, {
|
|
90
|
+
dialect,
|
|
91
|
+
});
|
|
92
|
+
this.evaluateValue(WARM_UP_EXPRESSION, WARM_UP_CONTEXT, WARM_UP_LABEL, {
|
|
93
|
+
budget: (0, expression_budget_1.createExpressionBudget)(WARM_UP_BUDGET),
|
|
94
|
+
dialect,
|
|
95
|
+
});
|
|
96
|
+
return [dialect, performance.now() - startedAt];
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// The error may carry host text; only a fixed code is written.
|
|
100
|
+
this.logger.warn(`code=EXPRESSION_WARM_UP_FAILED dialect=${dialect}`);
|
|
101
|
+
return [dialect, null];
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
return Object.fromEntries(entries);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Loads the dialect's engine and builds this registry's function
|
|
108
|
+
* environment now, outside any budget. Callers that are about to start a
|
|
109
|
+
* budgeted run (the dry run) call this first so an interrupt cannot land
|
|
110
|
+
* halfway through module loading or function registration.
|
|
111
|
+
*/
|
|
112
|
+
prepareEngine(dialect = expression_dialect_1.DEFAULT_EXPRESSION_DIALECT) {
|
|
113
|
+
const readDialect = (0, expression_dialect_1.readExpressionDialect)(dialect, 'expressionDialect');
|
|
25
114
|
try {
|
|
26
|
-
|
|
115
|
+
if (readDialect === 'cel-v2') {
|
|
116
|
+
(0, cel_v2_adapter_1.loadCelV2Environment)(this.functionRegistry);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
(0, cel_legacy_adapter_1.loadCelEngine)();
|
|
120
|
+
(0, cel_legacy_adapter_1.readCelLegacyFunctions)(this.functionRegistry);
|
|
27
121
|
}
|
|
28
122
|
catch (error) {
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
throw new Error(`[@rytass/bpm-core-nestjs-module] This Node version cannot require the ESM-only 'cel-js'. BPM needs Node >=20.19 or >=22.12 to evaluate CEL conditions. Original error: ${error instanceof Error ? error.message : String(error)}`);
|
|
123
|
+
// A registry that throws or answers out of shape is
|
|
124
|
+
// `EXPRESSION_FUNCTION_FAILED`, never the host's own error. An engine
|
|
125
|
+
// that fails to load is a configuration error and passes through.
|
|
126
|
+
if (error instanceof expression_engine_failure_1.ExpressionEngineFailure) {
|
|
127
|
+
throw (0, expression_errors_1.toExpressionRuntimeError)(error, 'expressionFunctions');
|
|
35
128
|
}
|
|
36
129
|
throw error;
|
|
37
130
|
}
|
|
38
131
|
}
|
|
39
|
-
return celEngine;
|
|
40
|
-
}
|
|
41
|
-
function isRequireEsmError(error) {
|
|
42
|
-
return (typeof error === 'object' &&
|
|
43
|
-
error !== null &&
|
|
44
|
-
'code' in error &&
|
|
45
|
-
error.code === 'ERR_REQUIRE_ESM');
|
|
46
|
-
}
|
|
47
|
-
const DEFAULT_MAX_EXPRESSION_LENGTH = 2000;
|
|
48
|
-
// `has` and `size` are CEL macros, not context identifiers; the root-identifier
|
|
49
|
-
// lint must not report them as unknown. `size` is required by the table field
|
|
50
|
-
// IS_FILLED / IS_EMPTY expressions (ADR 16 §3.8).
|
|
51
|
-
const CEL_KEYWORDS = new Set(['false', 'has', 'in', 'null', 'size', 'true']);
|
|
52
|
-
let ConditionService = class ConditionService {
|
|
53
132
|
lintExpressions(expressions, options = {}) {
|
|
54
133
|
return expressions.flatMap(({ expression, label }) => this.lintExpression(expression, label, options));
|
|
55
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* The lint as structured findings, warnings included (docs/24 §2.2): what
|
|
137
|
+
* the designer's preview and the publish lint read. `lintExpression` is the
|
|
138
|
+
* error half of it, as the lines publish has always returned.
|
|
139
|
+
*/
|
|
140
|
+
lintExpressionIssues(expression, label, options = {}) {
|
|
141
|
+
if (typeof expression === 'undefined' || expression === null) {
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
const trimmedExpression = expression.trim();
|
|
145
|
+
const maxExpressionLength = options.maxExpressionLength ?? DEFAULT_MAX_EXPRESSION_LENGTH;
|
|
146
|
+
if (!trimmedExpression) {
|
|
147
|
+
return [lintIssue('EXPR_PARSE_FAILED', label)];
|
|
148
|
+
}
|
|
149
|
+
if (trimmedExpression.length > maxExpressionLength) {
|
|
150
|
+
return [lintIssue('EXPR_COMPLEXITY_EXCEEDED', label)];
|
|
151
|
+
}
|
|
152
|
+
if (options.dialect !== undefined &&
|
|
153
|
+
!(0, expression_dialect_1.isExpressionDialect)(options.dialect)) {
|
|
154
|
+
return [lintIssue('EXPR_PARSE_FAILED', label)];
|
|
155
|
+
}
|
|
156
|
+
if (options.dialect === 'cel-v2') {
|
|
157
|
+
return (0, expression_ast_1.lintCelV2Expression)(trimmedExpression, label, {
|
|
158
|
+
...options,
|
|
159
|
+
functionRegistry: options.functionRegistry ?? this.functionRegistry,
|
|
160
|
+
}, this.functionRegistry);
|
|
161
|
+
}
|
|
162
|
+
if ((0, cel_legacy_adapter_1.parseCelLegacy)(trimmedExpression) === null) {
|
|
163
|
+
return [lintIssue('EXPR_PARSE_FAILED', label)];
|
|
164
|
+
}
|
|
165
|
+
return readUnsupportedRootIdentifiers(trimmedExpression, readLintRoots(options), 'cel-legacy').map((identifier) => lintIssue('EXPR_ROOT_NOT_ALLOWED', label, identifier));
|
|
166
|
+
}
|
|
56
167
|
lintExpression(expression, label, options = {}) {
|
|
57
168
|
if (typeof expression === 'undefined' || expression === null) {
|
|
58
169
|
return [];
|
|
59
170
|
}
|
|
171
|
+
// cel-v2 is linted on its AST (ADR 22 §3.4) and answers with the codes
|
|
172
|
+
// the preview reads (docs/24 §2.2), blank and over-long included; only
|
|
173
|
+
// the errors refuse, each as one `<label>: <code>` line.
|
|
174
|
+
if (options.dialect === 'cel-v2') {
|
|
175
|
+
return this.lintExpressionIssues(expression, label, options)
|
|
176
|
+
.filter((finding) => finding.severity === 'ERROR')
|
|
177
|
+
.map(bpm_core_shared_1.formatExpressionLintIssue);
|
|
178
|
+
}
|
|
60
179
|
const trimmedExpression = expression.trim();
|
|
61
180
|
const maxExpressionLength = options.maxExpressionLength ?? DEFAULT_MAX_EXPRESSION_LENGTH;
|
|
62
181
|
if (!trimmedExpression) {
|
|
@@ -65,60 +184,157 @@ let ConditionService = class ConditionService {
|
|
|
65
184
|
if (trimmedExpression.length > maxExpressionLength) {
|
|
66
185
|
return [`${label} must be ${maxExpressionLength} characters or fewer`];
|
|
67
186
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
187
|
+
// A dialect this version does not know is not guessed at: parsing with
|
|
188
|
+
// the wrong engine could pass what the right one refuses.
|
|
189
|
+
if (options.dialect !== undefined &&
|
|
190
|
+
!(0, expression_dialect_1.isExpressionDialect)(options.dialect)) {
|
|
191
|
+
return [`${label}: EXPRESSION_SYNTAX_ERROR`];
|
|
71
192
|
}
|
|
72
|
-
|
|
193
|
+
// The engine's parse message is not returned: it can echo the expression,
|
|
194
|
+
// and the lint result reaches GraphQL clients (ADR 22 §3.2).
|
|
195
|
+
if ((0, cel_legacy_adapter_1.parseCelLegacy)(trimmedExpression) === null) {
|
|
196
|
+
return [`${label}: EXPRESSION_SYNTAX_ERROR`];
|
|
197
|
+
}
|
|
198
|
+
return lintRootIdentifiers(trimmedExpression, label, readLintRoots(options), 'cel-legacy');
|
|
73
199
|
}
|
|
74
|
-
evaluateBoolean(expression, context, label) {
|
|
200
|
+
evaluateBoolean(expression, context, label, options = {}) {
|
|
75
201
|
if (typeof expression !== 'string' || !expression.trim()) {
|
|
76
202
|
return true;
|
|
77
203
|
}
|
|
78
|
-
const value = this.evaluateValue(expression, context, label);
|
|
204
|
+
const value = this.evaluateValue(expression, context, label, options);
|
|
79
205
|
if (typeof value === 'boolean') {
|
|
80
206
|
return value;
|
|
81
207
|
}
|
|
82
|
-
throw new
|
|
208
|
+
throw new expression_errors_1.ExpressionRuntimeError('EXPRESSION_TYPE_ERROR', label, {
|
|
209
|
+
detail: 'must evaluate to a boolean',
|
|
210
|
+
});
|
|
83
211
|
}
|
|
84
|
-
evaluateValue(expression, context, label) {
|
|
212
|
+
evaluateValue(expression, context, label, options = {}) {
|
|
85
213
|
const trimmedExpression = expression.trim();
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
catch (error) {
|
|
99
|
-
throw new common_1.BadRequestException(error instanceof Error ? `${label}: ${error.message}` : label);
|
|
100
|
-
}
|
|
214
|
+
const dialect = (0, expression_dialect_1.readExpressionDialect)(options.dialect, label);
|
|
215
|
+
assertEvaluable(trimmedExpression, label, options.allowedRootIdentifiers, dialect);
|
|
216
|
+
return this.runtime.evaluate({
|
|
217
|
+
budget: options.budget ??
|
|
218
|
+
(0, expression_budget_1.currentExpressionBudget)() ??
|
|
219
|
+
(0, expression_budget_1.createExpressionBudget)(expression_budget_1.DEFAULT_EXPRESSION_BUDGET),
|
|
220
|
+
dialect,
|
|
221
|
+
expression: trimmedExpression,
|
|
222
|
+
label,
|
|
223
|
+
roots: pickAllowedRoots(context, options.allowedRootIdentifiers),
|
|
224
|
+
});
|
|
101
225
|
}
|
|
102
226
|
};
|
|
103
227
|
exports.ConditionService = ConditionService;
|
|
104
|
-
exports.ConditionService = ConditionService = tslib_1.__decorate([
|
|
105
|
-
(0, common_1.Injectable)()
|
|
228
|
+
exports.ConditionService = ConditionService = ConditionService_1 = tslib_1.__decorate([
|
|
229
|
+
(0, common_1.Injectable)(),
|
|
230
|
+
tslib_1.__param(0, (0, common_1.Optional)()),
|
|
231
|
+
tslib_1.__param(0, (0, common_1.Inject)(expression_function_types_1.BPM_EXPRESSION_FUNCTION_REGISTRY)),
|
|
232
|
+
tslib_1.__metadata("design:paramtypes", [Object])
|
|
106
233
|
], ConditionService);
|
|
107
|
-
|
|
108
|
-
|
|
234
|
+
/**
|
|
235
|
+
* The roots handed to the engine: only the allowed ones when the use point has
|
|
236
|
+
* an allowlist. The textual root check cannot see a macro variable's scope (it
|
|
237
|
+
* skips the bound name everywhere in the expression), so this is what keeps a
|
|
238
|
+
* root the use point may not read out of reach, whatever else the caller's
|
|
239
|
+
* context holds. Without an allowlist the context is passed as given.
|
|
240
|
+
*/
|
|
241
|
+
function pickAllowedRoots(context, allowedRootIdentifiers) {
|
|
242
|
+
if (!allowedRootIdentifiers?.length) {
|
|
243
|
+
return context;
|
|
244
|
+
}
|
|
245
|
+
return Object.fromEntries(allowedRootIdentifiers
|
|
246
|
+
.filter((identifier) => Object.prototype.hasOwnProperty.call(context, identifier))
|
|
247
|
+
.map((identifier) => [
|
|
248
|
+
identifier,
|
|
249
|
+
context[identifier],
|
|
250
|
+
]));
|
|
109
251
|
}
|
|
110
|
-
|
|
111
|
-
|
|
252
|
+
/**
|
|
253
|
+
* Guards that run before anything is parsed or evaluated. A refusal is an
|
|
254
|
+
* `EXPRESSION_SYNTAX_ERROR`: the expression is not one this runtime will run.
|
|
255
|
+
*/
|
|
256
|
+
function assertEvaluable(expression, label, allowedRootIdentifiers, dialect) {
|
|
257
|
+
if (!expression || expression.length > DEFAULT_MAX_EXPRESSION_LENGTH) {
|
|
258
|
+
throw new expression_errors_1.ExpressionRuntimeError('EXPRESSION_SYNTAX_ERROR', label);
|
|
259
|
+
}
|
|
260
|
+
if (lintRootIdentifiers(expression, label, allowedRootIdentifiers, dialect)
|
|
261
|
+
.length > 0) {
|
|
262
|
+
throw new expression_errors_1.ExpressionRuntimeError('EXPRESSION_SYNTAX_ERROR', label);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function lintIssue(code, label, subject) {
|
|
266
|
+
return {
|
|
267
|
+
code,
|
|
268
|
+
label,
|
|
269
|
+
severity: (0, bpm_core_shared_1.readExpressionLintSeverity)(code),
|
|
270
|
+
...(subject === undefined ? {} : { subject }),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* The cel-legacy lint's roots. A use point that is set but is not one (a
|
|
275
|
+
* client may send `toString`) is never used as an index: it allows nothing
|
|
276
|
+
* beyond the keywords the scan always skips, so every root is reported. An
|
|
277
|
+
* empty list would not do that: `readUnsupportedRootIdentifiers` reads it as
|
|
278
|
+
* "do not check".
|
|
279
|
+
*/
|
|
280
|
+
function readLintRoots(options) {
|
|
281
|
+
if (options.allowedRootIdentifiers?.length) {
|
|
282
|
+
return options.allowedRootIdentifiers;
|
|
283
|
+
}
|
|
284
|
+
if (options.usagePoint !== undefined &&
|
|
285
|
+
options.usagePoint !== null &&
|
|
286
|
+
!(0, bpm_core_shared_1.isExpressionUsagePoint)(options.usagePoint)) {
|
|
287
|
+
return [...CEL_KEYWORDS];
|
|
288
|
+
}
|
|
289
|
+
return options.usagePoint
|
|
290
|
+
? bpm_core_shared_1.EXPRESSION_ROOT_IDENTIFIERS_BY_USAGE_POINT[options.usagePoint]
|
|
291
|
+
: options.allowedRootIdentifiers;
|
|
292
|
+
}
|
|
293
|
+
// cel-v2 macros that bind a variable as their first argument
|
|
294
|
+
// (`list.map(x, ...)`, `list.exists(x, ...)` ...).
|
|
295
|
+
const CEL_V2_BINDING_MACRO_PATTERN = /\.\s*(?:all|exists|exists_one|filter|map)\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*,/gu;
|
|
296
|
+
/**
|
|
297
|
+
* Root identifiers the expression reads that its use point does not allow,
|
|
298
|
+
* found by a textual scan.
|
|
299
|
+
*
|
|
300
|
+
* The lint uses this for cel-legacy only, which has no AST to read (docs/24
|
|
301
|
+
* §1); cel-v2 is linted on its AST (`lintCelV2Expression`), where a name a
|
|
302
|
+
* macro binds is in scope only inside that macro.
|
|
303
|
+
*
|
|
304
|
+
* Evaluation still runs this scan before handing an expression to either
|
|
305
|
+
* engine (`assertEvaluable`). For cel-v2 it skips what that dialect adds and
|
|
306
|
+
* is not a root: names a macro binds (`a` in `form.l.map(a, a.qty)`) and
|
|
307
|
+
* global function calls (`int(form.x)`). A bound name is skipped everywhere in
|
|
308
|
+
* the expression, not only inside its macro, so
|
|
309
|
+
* `[1].exists(lastDecision, true) && lastDecision.d == ""` passes the scan
|
|
310
|
+
* where `lastDecision` is not allowed. The publish lint refuses that on the
|
|
311
|
+
* AST; at evaluation `evaluateValue` hands the engine only the allowed roots
|
|
312
|
+
* (`pickAllowedRoots`), so the name outside the macro is a missing key.
|
|
313
|
+
*/
|
|
314
|
+
function readUnsupportedRootIdentifiers(expression, allowedRootIdentifiers, dialect = expression_dialect_1.DEFAULT_EXPRESSION_DIALECT) {
|
|
112
315
|
if (!allowedRootIdentifiers?.length) {
|
|
113
316
|
return [];
|
|
114
317
|
}
|
|
115
|
-
const allowed = new Set([...allowedRootIdentifiers, ...CEL_KEYWORDS]);
|
|
116
318
|
const sanitizedExpression = stripStringLiterals(expression);
|
|
117
|
-
const
|
|
118
|
-
const
|
|
319
|
+
const isV2 = dialect === 'cel-v2';
|
|
320
|
+
const boundNames = isV2
|
|
321
|
+
? Array.from(sanitizedExpression.matchAll(CEL_V2_BINDING_MACRO_PATTERN)).map((match) => match[1])
|
|
322
|
+
: [];
|
|
323
|
+
const allowed = new Set([
|
|
324
|
+
...allowedRootIdentifiers,
|
|
325
|
+
...CEL_KEYWORDS,
|
|
326
|
+
...boundNames,
|
|
327
|
+
]);
|
|
328
|
+
const identifiers = Array.from(sanitizedExpression.matchAll(/(^|[^A-Za-z0-9_.])([A-Za-z_][A-Za-z0-9_]*)/gu))
|
|
329
|
+
.filter((match) => !isV2 ||
|
|
330
|
+
!/^\s*\(/u.test(sanitizedExpression.slice((match.index ?? 0) + match[0].length)))
|
|
331
|
+
.map((match) => match[2]);
|
|
332
|
+
return [
|
|
119
333
|
...new Set(identifiers.filter((identifier) => !allowed.has(identifier))),
|
|
120
334
|
];
|
|
121
|
-
|
|
335
|
+
}
|
|
336
|
+
function lintRootIdentifiers(expression, label, allowedRootIdentifiers, dialect = expression_dialect_1.DEFAULT_EXPRESSION_DIALECT) {
|
|
337
|
+
return readUnsupportedRootIdentifiers(expression, allowedRootIdentifiers, dialect).map((identifier) => `${label} references unsupported identifier ${identifier}`);
|
|
122
338
|
}
|
|
123
339
|
function stripStringLiterals(expression) {
|
|
124
340
|
return expression.replace(/"([^"\\]|\\.)*"|'([^'\\]|\\.)*'/gu, '""');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"condition.service.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/condition/condition.service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"condition.service.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/condition/condition.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { ExpressionLintIssue, ExpressionUsagePoint } from '@rytass/bpm-core-shared';
|
|
2
|
+
import { FormDefinitionSchema, FormFieldDefinition } from '@rytass/bpm-core-shared/form';
|
|
3
|
+
import type { BPMExpressionFunctionRegistry } from '../expression-function/expression-function.types';
|
|
4
|
+
import { ExpressionDialect } from './expression-dialect';
|
|
5
|
+
/**
|
|
6
|
+
* A chain of member and index steps from a root: `form.items[0].qty` is
|
|
7
|
+
* `{ root: 'form', segments: ['items', '0', 'qty'] }`. `segments` stops at the
|
|
8
|
+
* first step that is not a literal (a computed index or a method call), and
|
|
9
|
+
* `complete` is then `false`.
|
|
10
|
+
*/
|
|
11
|
+
export interface ExpressionFieldPath {
|
|
12
|
+
readonly complete: boolean;
|
|
13
|
+
readonly root: string;
|
|
14
|
+
readonly segments: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
export interface ExpressionFunctionCall {
|
|
17
|
+
readonly argumentCount: number;
|
|
18
|
+
readonly name: string;
|
|
19
|
+
/** `true` for a method call (`x.contains(y)`), `false` for `f(x)`. */
|
|
20
|
+
readonly receiver: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A method or macro called directly on a path from a root:
|
|
24
|
+
* `form.items.exists(r, ...)` is `{ name: 'exists', path: form.items }`.
|
|
25
|
+
*/
|
|
26
|
+
export interface ExpressionReceiverPath {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly path: ExpressionFieldPath;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A key that is not a literal: an index (`form[k]`, `form.a[form.b]`) or a map
|
|
32
|
+
* literal key (`{form.k: 1}`). `root` is the root the indexed chain starts at,
|
|
33
|
+
* `null` for map keys and chains that start elsewhere.
|
|
34
|
+
*/
|
|
35
|
+
export interface ExpressionComputedKey {
|
|
36
|
+
readonly kind: 'INDEX' | 'MAP_KEY';
|
|
37
|
+
readonly root: string | null;
|
|
38
|
+
}
|
|
39
|
+
export interface ExpressionReferences {
|
|
40
|
+
/** Highest nesting of `all`/`exists`/`exists_one`/`map`/`filter`. */
|
|
41
|
+
readonly comprehensionDepth: number;
|
|
42
|
+
readonly computedKeys: readonly ExpressionComputedKey[];
|
|
43
|
+
readonly dialect: ExpressionDialect;
|
|
44
|
+
/** Paths read on the right of a `/`. */
|
|
45
|
+
readonly divisorFieldPaths: readonly ExpressionFieldPath[];
|
|
46
|
+
readonly fieldPaths: readonly ExpressionFieldPath[];
|
|
47
|
+
/** Top-level form field keys read through `form` or `formData`. */
|
|
48
|
+
readonly formFieldKeys: readonly string[];
|
|
49
|
+
readonly functionCalls: readonly ExpressionFunctionCall[];
|
|
50
|
+
/** Paths tested by `has(...)` or compared with `null`, `""` or `[]`. */
|
|
51
|
+
readonly guardedFieldPaths: readonly ExpressionFieldPath[];
|
|
52
|
+
/** The arguments of `has(...)`. */
|
|
53
|
+
readonly hasArguments: readonly ExpressionFieldPath[];
|
|
54
|
+
/**
|
|
55
|
+
* Parsed nodes. cel-v2 counts AST nodes (the engine refuses more than
|
|
56
|
+
* 200); cel-legacy counts CST atoms, steps and operators, which is a
|
|
57
|
+
* different measure and only comparable with itself.
|
|
58
|
+
*/
|
|
59
|
+
readonly nodeCount: number;
|
|
60
|
+
/** Paths read inside an operand of `||`. */
|
|
61
|
+
readonly orOperandFieldPaths: readonly ExpressionFieldPath[];
|
|
62
|
+
readonly receiverPaths: readonly ExpressionReceiverPath[];
|
|
63
|
+
/** `__proto__`, `constructor` or `prototype` used as a member, key or map key. */
|
|
64
|
+
readonly reservedSegments: readonly string[];
|
|
65
|
+
/** Free identifiers: names the expression reads that nothing in it binds. */
|
|
66
|
+
readonly roots: readonly string[];
|
|
67
|
+
/** Roots used as a value themselves, not as the start of a path. */
|
|
68
|
+
readonly wholeRootReferences: readonly string[];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* What `readExpressionReferences` found. `UNAVAILABLE` is a cel-legacy
|
|
72
|
+
* expression whose parser returned no CST to walk (the unit tests' `cel-js`
|
|
73
|
+
* shim does that): per docs/24 §1 the old dialect then keeps its textual lint.
|
|
74
|
+
*/
|
|
75
|
+
export type ExpressionReferencesResult = {
|
|
76
|
+
readonly references: ExpressionReferences;
|
|
77
|
+
readonly status: 'OK';
|
|
78
|
+
} | {
|
|
79
|
+
readonly limitExceeded: boolean;
|
|
80
|
+
readonly status: 'PARSE_FAILED';
|
|
81
|
+
} | {
|
|
82
|
+
readonly status: 'UNAVAILABLE';
|
|
83
|
+
};
|
|
84
|
+
export declare const RESERVED_PATH_SEGMENTS: ReadonlySet<string>;
|
|
85
|
+
/** ADR 22 §3.4 / docs/24 §2.7. */
|
|
86
|
+
export declare const EXPRESSION_MAX_COMPREHENSION_DEPTH = 2;
|
|
87
|
+
/** Unique top-level keys of the paths rooted at `form` or `formData`, in first-seen order. */
|
|
88
|
+
export declare const readFormFieldKeys: (paths: readonly ExpressionFieldPath[]) => readonly string[];
|
|
89
|
+
/**
|
|
90
|
+
* The roots, field paths, function calls and structure an expression uses,
|
|
91
|
+
* read from its AST without evaluating it. Names a comprehension binds (`x`
|
|
92
|
+
* in `form.items.exists(x, x.qty > 0)`) are only in scope inside it: the same
|
|
93
|
+
* name outside it is a root.
|
|
94
|
+
*
|
|
95
|
+
* Works for both dialects. cel-v2 reads the engine's AST; cel-legacy walks the
|
|
96
|
+
* CST of `cel-js` and answers `UNAVAILABLE` when there is none to walk.
|
|
97
|
+
*/
|
|
98
|
+
export declare function readExpressionReferences(expression: string, dialect?: ExpressionDialect): ExpressionReferencesResult;
|
|
99
|
+
/** Whether the expression reads the top-level form field `fieldKey`. */
|
|
100
|
+
export declare const readsFormField: (references: ExpressionReferences, fieldKey: string) => boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Whether the expression steps inside the table field `tableKey`
|
|
103
|
+
* (`form.items.qty`, `form.items[0]`, `form.items.exists(r, ...)`), the AST
|
|
104
|
+
* twin of `referencesTableInternals` (ADR 16 §3.8). The table as a whole
|
|
105
|
+
* (`size(form.items)`, `form.items.size()`, `form.items == null`) is not.
|
|
106
|
+
*/
|
|
107
|
+
export declare const readsTableInternals: (references: ExpressionReferences, tableKey: string) => boolean;
|
|
108
|
+
/** The slice of the function registry the lint uses (docs/24 §3: P3 implements it). */
|
|
109
|
+
export type ExpressionFunctionAvailability = Pick<BPMExpressionFunctionRegistry, 'assertPublishable' | 'get'>;
|
|
110
|
+
export interface ExpressionLintContext {
|
|
111
|
+
/** Overrides the use point's roots. Without either, roots are not checked. */
|
|
112
|
+
readonly allowedRootIdentifiers?: readonly string[];
|
|
113
|
+
/**
|
|
114
|
+
* The bound form. With it, field keys are checked against the schema and
|
|
115
|
+
* the type check knows each field's type; without it every field is `dyn`.
|
|
116
|
+
*/
|
|
117
|
+
readonly formSchema?: FormDefinitionSchema | null;
|
|
118
|
+
/**
|
|
119
|
+
* The registry that answers which functions are available. Absent means
|
|
120
|
+
* only the nine built-ins exist (`BPM_BUILT_IN_EXPRESSION_FUNCTION_NAMES`);
|
|
121
|
+
* `ConditionService` fills it with the registry it was given. Host code:
|
|
122
|
+
* a throw or a malformed answer is isolated and refuses the names asked
|
|
123
|
+
* about (`EXPR_FUNCTION_NOT_REGISTERED`), never its message.
|
|
124
|
+
*/
|
|
125
|
+
readonly functionRegistry?: ExpressionFunctionAvailability | null;
|
|
126
|
+
/** The field a `SET_FORM_FIELD` writes; decides its result type. */
|
|
127
|
+
readonly targetField?: FormFieldDefinition | null;
|
|
128
|
+
readonly usagePoint?: ExpressionUsagePoint;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The CEL types a form field is declared with for the lint's type check
|
|
132
|
+
* (docs/24-design/p2-p3 §2.2). Only names the engine accepts are produced;
|
|
133
|
+
* anything else is `dyn`. Never `optional(...)`: the engine cannot convert it.
|
|
134
|
+
*/
|
|
135
|
+
export declare const readExpressionFieldType: (field: FormFieldDefinition) => string;
|
|
136
|
+
/**
|
|
137
|
+
* The called function names the registry cannot vouch for, in first-seen
|
|
138
|
+
* order and without duplicates (ADR 22 §5.3; publish and rollback share it).
|
|
139
|
+
*
|
|
140
|
+
* Only names {@link isRegistryFunctionName} accepts — `…_vN` and the nine
|
|
141
|
+
* built-ins — are put to `assertPublishable`; any other name can never be
|
|
142
|
+
* registered and is always reported. Without a registry only the built-ins
|
|
143
|
+
* exist.
|
|
144
|
+
*
|
|
145
|
+
* The registry is host code and is isolated: an `assertPublishable` that
|
|
146
|
+
* throws or answers something other than a list of strings makes every name
|
|
147
|
+
* asked about unavailable. The result only ever holds names the expression
|
|
148
|
+
* itself called, never a value the registry returned.
|
|
149
|
+
*/
|
|
150
|
+
export declare function readUnavailableExpressionFunctions(names: readonly string[], registry: ExpressionFunctionAvailability | null | undefined): readonly string[];
|
|
151
|
+
/**
|
|
152
|
+
* The cel-v2 lint of one expression (ADR 22 §3.4, docs/24 §2.2): errors that
|
|
153
|
+
* refuse a publish and warnings that do not, in that order. The expression is
|
|
154
|
+
* expected trimmed and within the length limit (`ConditionService` checks
|
|
155
|
+
* both first).
|
|
156
|
+
*
|
|
157
|
+
* `environmentRegistry` is the registry evaluation will run with (the one
|
|
158
|
+
* `ConditionService` was given): the expression is parsed and type-checked in
|
|
159
|
+
* its cel-v2 environment, so the built-ins and the host's functions are
|
|
160
|
+
* type-checked like the engine's own. Absent means the built-ins only. It is
|
|
161
|
+
* separate from `context.functionRegistry`, which only answers availability
|
|
162
|
+
* (`assertPublishable`, `get`) and need not be able to build an environment.
|
|
163
|
+
*
|
|
164
|
+
* The type check (`env.check()`) runs only when nothing structural is wrong
|
|
165
|
+
* and every function called is one that environment knows; a call to one it
|
|
166
|
+
* does not know is covered by `EXPR_FUNCTION_NOT_REGISTERED` instead.
|
|
167
|
+
*/
|
|
168
|
+
export declare function lintCelV2Expression(expression: string, label: string, context?: ExpressionLintContext, environmentRegistry?: BPMExpressionFunctionRegistry | null): readonly ExpressionLintIssue[];
|
|
169
|
+
/**
|
|
170
|
+
* The global function names an expression calls that cel-v2 does not provide
|
|
171
|
+
* itself: the built-ins and host functions a rollback must find registered
|
|
172
|
+
* (docs/24 §1). `null` when the expression cannot be read. Hand the result to
|
|
173
|
+
* {@link readUnavailableExpressionFunctions}, which applies the same name
|
|
174
|
+
* rule and registry isolation as the publish lint.
|
|
175
|
+
*/
|
|
176
|
+
export declare function readExpressionHostFunctionNames(expression: string, dialect?: ExpressionDialect): readonly string[] | null;
|