@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
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.callsUnavailableFunction = exports.isRegistryFunctionName = exports.readExpressionFunctionTable = exports.createExpressionFunctionInvoker = exports.runWithExpressionFunctionFrame = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
const bpm_core_shared_1 = require("@rytass/bpm-core-shared");
|
|
6
|
+
const expression_function_registry_1 = require("../expression-function/expression-function.registry");
|
|
7
|
+
const expression_engine_failure_1 = require("./expression-engine-failure");
|
|
8
|
+
const expression_result_walker_1 = require("./expression-result.walker");
|
|
9
|
+
/*
|
|
10
|
+
* The one place a function an expression calls is run (ADR 22 §3.5). Both
|
|
11
|
+
* engine adapters register the wrappers built here, so the two dialects share
|
|
12
|
+
* one set of rules:
|
|
13
|
+
*
|
|
14
|
+
* - arguments become fresh deep-frozen JSON copies (the walker), so a handler
|
|
15
|
+
* can never reach `formData`; writing to one throws `TypeError` in strict mode;
|
|
16
|
+
* - an argument that does not fit the declared parameter type is
|
|
17
|
+
* `EXPRESSION_TYPE_ERROR`, as the engine reports an overload mismatch;
|
|
18
|
+
* - a handler that throws, returns a thenable, or returns something that is
|
|
19
|
+
* not JSON or not its `returnType` is `EXPRESSION_FUNCTION_FAILED`, and its
|
|
20
|
+
* message is never kept: only the function name and the code are logged;
|
|
21
|
+
* - the handler runs inside the evaluation, so its time is charged to the
|
|
22
|
+
* expression budget and a handler that does not finish is interrupted
|
|
23
|
+
* (`EXPRESSION_BUDGET_EXCEEDED`, the interrupt skips every catch here).
|
|
24
|
+
*/
|
|
25
|
+
const logger = new common_1.Logger('ExpressionFunction');
|
|
26
|
+
let activeFrame = null;
|
|
27
|
+
/**
|
|
28
|
+
* Runs one engine evaluation and fails it with `EXPRESSION_FUNCTION_FAILED`
|
|
29
|
+
* when any function it called failed, even if the engine absorbed that error.
|
|
30
|
+
*/
|
|
31
|
+
const runWithExpressionFunctionFrame = (work) => {
|
|
32
|
+
const previous = activeFrame;
|
|
33
|
+
// Mutable on purpose: the wrappers below flag it while the engine runs.
|
|
34
|
+
const frame = { failed: false };
|
|
35
|
+
activeFrame = frame;
|
|
36
|
+
try {
|
|
37
|
+
const result = work();
|
|
38
|
+
if (frame.failed) {
|
|
39
|
+
throw new expression_engine_failure_1.ExpressionEngineFailure('EXPRESSION_FUNCTION_FAILED');
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (frame.failed) {
|
|
45
|
+
throw new expression_engine_failure_1.ExpressionEngineFailure('EXPRESSION_FUNCTION_FAILED');
|
|
46
|
+
}
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
activeFrame = previous;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
exports.runWithExpressionFunctionFrame = runWithExpressionFunctionFrame;
|
|
54
|
+
const failFunction = (name, reason) => {
|
|
55
|
+
if (activeFrame !== null) {
|
|
56
|
+
activeFrame.failed = true;
|
|
57
|
+
}
|
|
58
|
+
// The name is the host's validated identifier and the reason a fixed word;
|
|
59
|
+
// neither the handler's message nor any argument is ever written.
|
|
60
|
+
logger.warn(`function=${name} code=EXPRESSION_FUNCTION_FAILED reason=${reason}`);
|
|
61
|
+
throw new expression_engine_failure_1.ExpressionEngineFailure('EXPRESSION_FUNCTION_FAILED');
|
|
62
|
+
};
|
|
63
|
+
const refuseArgument = () => {
|
|
64
|
+
throw new expression_engine_failure_1.ExpressionEngineFailure('EXPRESSION_TYPE_ERROR');
|
|
65
|
+
};
|
|
66
|
+
const isPlainMap = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
67
|
+
/** Whether a JSON value is of a declared type. `dyn` accepts any JSON value. */
|
|
68
|
+
const isOfType = (value, type) => {
|
|
69
|
+
switch (type) {
|
|
70
|
+
case 'bool':
|
|
71
|
+
return typeof value === 'boolean';
|
|
72
|
+
case 'double':
|
|
73
|
+
return typeof value === 'number';
|
|
74
|
+
case 'dyn':
|
|
75
|
+
return true;
|
|
76
|
+
case 'int':
|
|
77
|
+
return typeof value === 'number' && Number.isInteger(value);
|
|
78
|
+
case 'list':
|
|
79
|
+
return Array.isArray(value);
|
|
80
|
+
case 'map':
|
|
81
|
+
return isPlainMap(value);
|
|
82
|
+
case 'null':
|
|
83
|
+
return value === null;
|
|
84
|
+
case 'string':
|
|
85
|
+
return typeof value === 'string';
|
|
86
|
+
default:
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* One argument as the handler sees it: a fresh deep-frozen JSON copy (cel-v2
|
|
92
|
+
* `int`s become JS numbers). A value that is not JSON — cel-v2 bytes, a
|
|
93
|
+
* timestamp, a value beyond the result limits — is a type error at the call.
|
|
94
|
+
*/
|
|
95
|
+
const readArgument = (name, raw, type) => {
|
|
96
|
+
let value;
|
|
97
|
+
try {
|
|
98
|
+
value = (0, expression_result_walker_1.normalizeExpressionResult)(raw, name, expression_result_walker_1.DEFAULT_EXPRESSION_RESULT_LIMITS);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return refuseArgument();
|
|
102
|
+
}
|
|
103
|
+
return isOfType(value, type) ? value : refuseArgument();
|
|
104
|
+
};
|
|
105
|
+
const isThenable = (value) => (typeof value === 'object' || typeof value === 'function') &&
|
|
106
|
+
value !== null &&
|
|
107
|
+
typeof value.then === 'function';
|
|
108
|
+
const ignoreRejection = () => undefined;
|
|
109
|
+
/**
|
|
110
|
+
* Marks a native promise's rejection as handled, so a rejection nobody awaits
|
|
111
|
+
* does not become an unhandled rejection in the host process.
|
|
112
|
+
*
|
|
113
|
+
* Only a real promise is touched, and through the intrinsic
|
|
114
|
+
* `Promise.prototype.then`: its brand check throws for anything else before
|
|
115
|
+
* any host code runs, and an own `then` the host put on a promise is never
|
|
116
|
+
* called. A foreign thenable is deliberately not adopted — `Promise.resolve`
|
|
117
|
+
* would call its `then` in a later microtask, after the evaluation and outside
|
|
118
|
+
* the budget, where it could block the event loop for as long as it likes.
|
|
119
|
+
* Whatever does run here (a promise subclass's species constructor) runs now,
|
|
120
|
+
* inside the evaluation and its budget.
|
|
121
|
+
*/
|
|
122
|
+
const silenceNativeRejection = (value) => {
|
|
123
|
+
try {
|
|
124
|
+
Reflect.apply(Promise.prototype.then, value, [undefined, ignoreRejection]);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Not a native promise, or a subclass that refused: nothing to silence.
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* What the engine gets back: the checked JSON result, with a declared `int`
|
|
132
|
+
* turned into a `bigint` for cel-v2, whose `int` is a `bigint` (a JS number
|
|
133
|
+
* there is a `double`, and `int` results would silently compare unequal).
|
|
134
|
+
*/
|
|
135
|
+
const toEngineValue = (value, returnType, dialect) => dialect === 'cel-v2' && returnType === 'int' && typeof value === 'number'
|
|
136
|
+
? BigInt(value)
|
|
137
|
+
: value;
|
|
138
|
+
/**
|
|
139
|
+
* Wraps a registered function for one dialect's engine. The wrapper is what
|
|
140
|
+
* the engine calls; the handler only ever sees checked, frozen arguments.
|
|
141
|
+
*/
|
|
142
|
+
const createExpressionFunctionInvoker = (fn, dialect) => (...rawArgs) => {
|
|
143
|
+
// cel-v2 enforces arity through the registered signature; cel-legacy
|
|
144
|
+
// passes whatever was written.
|
|
145
|
+
if (rawArgs.length !== fn.parameters.length) {
|
|
146
|
+
return refuseArgument();
|
|
147
|
+
}
|
|
148
|
+
const args = rawArgs.map((raw, index) => readArgument(fn.name, raw, fn.parameters[index].type));
|
|
149
|
+
let raw;
|
|
150
|
+
let thenable;
|
|
151
|
+
try {
|
|
152
|
+
raw = Reflect.apply(fn.handler, undefined, args);
|
|
153
|
+
// Inside the `try`: reading `then` can run a host getter.
|
|
154
|
+
thenable = isThenable(raw);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// Whatever the handler threw — a `TypeError` from writing to a frozen
|
|
158
|
+
// argument, a host error, even an `ExpressionRuntimeError` that would
|
|
159
|
+
// otherwise pass through with its own label and code — becomes one code.
|
|
160
|
+
return failFunction(fn.name, 'threw');
|
|
161
|
+
}
|
|
162
|
+
if (thenable) {
|
|
163
|
+
// Nobody will await it; without a handler a later rejection would be an
|
|
164
|
+
// unhandled rejection in the host process.
|
|
165
|
+
silenceNativeRejection(raw);
|
|
166
|
+
return failFunction(fn.name, 'thenable');
|
|
167
|
+
}
|
|
168
|
+
let value;
|
|
169
|
+
try {
|
|
170
|
+
value = (0, expression_result_walker_1.normalizeExpressionResult)(raw, fn.name, expression_result_walker_1.DEFAULT_EXPRESSION_RESULT_LIMITS);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return failFunction(fn.name, 'invalid-result');
|
|
174
|
+
}
|
|
175
|
+
return isOfType(value, fn.returnType)
|
|
176
|
+
? toEngineValue(value, fn.returnType, dialect)
|
|
177
|
+
: failFunction(fn.name, 'return-type');
|
|
178
|
+
};
|
|
179
|
+
exports.createExpressionFunctionInvoker = createExpressionFunctionInvoker;
|
|
180
|
+
const tables = new WeakMap();
|
|
181
|
+
/** Thrown inside the table read when the registry answered out of shape. */
|
|
182
|
+
class RegistryShapeError extends Error {
|
|
183
|
+
}
|
|
184
|
+
const VALUE_TYPES = new Set(expression_function_registry_1.BPM_EXPRESSION_VALUE_TYPES);
|
|
185
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
186
|
+
const refuseShape = () => {
|
|
187
|
+
throw new RegistryShapeError();
|
|
188
|
+
};
|
|
189
|
+
const readValueType = (value) => typeof value === 'string' && VALUE_TYPES.has(value)
|
|
190
|
+
? value
|
|
191
|
+
: refuseShape();
|
|
192
|
+
/**
|
|
193
|
+
* A frozen copy of what `get` returned for `name`, or a shape refusal. The
|
|
194
|
+
* copy is what the wrappers read, so a registry that changes the object it
|
|
195
|
+
* returned later changes nothing here.
|
|
196
|
+
*/
|
|
197
|
+
const readRegisteredFunction = (candidate, name) => {
|
|
198
|
+
if (!isRecord(candidate) || candidate.name !== name) {
|
|
199
|
+
return refuseShape();
|
|
200
|
+
}
|
|
201
|
+
const { description, handler, label, parameters, returnType } = candidate;
|
|
202
|
+
if (typeof handler !== 'function' || !Array.isArray(parameters)) {
|
|
203
|
+
return refuseShape();
|
|
204
|
+
}
|
|
205
|
+
return Object.freeze({
|
|
206
|
+
description: typeof description === 'string' ? description : '',
|
|
207
|
+
handler: handler,
|
|
208
|
+
label: typeof label === 'string' ? label : name,
|
|
209
|
+
name,
|
|
210
|
+
parameters: Object.freeze(parameters.map((parameter) => isRecord(parameter) && typeof parameter.name === 'string'
|
|
211
|
+
? Object.freeze({
|
|
212
|
+
name: parameter.name,
|
|
213
|
+
type: readValueType(parameter.type),
|
|
214
|
+
})
|
|
215
|
+
: refuseShape())),
|
|
216
|
+
returnType: readValueType(returnType),
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
const failRegistry = (reason) => {
|
|
220
|
+
logger.warn(`code=EXPRESSION_FUNCTION_FAILED reason=${reason}`);
|
|
221
|
+
throw new expression_engine_failure_1.ExpressionEngineFailure('EXPRESSION_FUNCTION_FAILED');
|
|
222
|
+
};
|
|
223
|
+
const readListedNames = (listed) => {
|
|
224
|
+
if (!Array.isArray(listed)) {
|
|
225
|
+
return refuseShape();
|
|
226
|
+
}
|
|
227
|
+
const names = listed.map((descriptor) => isRecord(descriptor) &&
|
|
228
|
+
typeof descriptor.name === 'string' &&
|
|
229
|
+
(0, exports.isRegistryFunctionName)(descriptor.name)
|
|
230
|
+
? descriptor.name
|
|
231
|
+
: refuseShape());
|
|
232
|
+
// A name listed twice would register two overloads of one signature.
|
|
233
|
+
return new Set(names).size === names.length ? names : refuseShape();
|
|
234
|
+
};
|
|
235
|
+
/**
|
|
236
|
+
* Reads every function a registry lists, through `get`, once per registry.
|
|
237
|
+
*
|
|
238
|
+
* The registry is host code (a host may provide its own under
|
|
239
|
+
* `BPM_EXPRESSION_FUNCTION_REGISTRY`). If `list` or `get` throws, or answers
|
|
240
|
+
* out of shape — not a list, a name that is not a function name or is listed
|
|
241
|
+
* twice, `null` for a listed name, an object without a handler or with an
|
|
242
|
+
* unknown type — the evaluation fails with `EXPRESSION_FUNCTION_FAILED`. The
|
|
243
|
+
* host's message is never kept: only a fixed reason is logged. A failure is
|
|
244
|
+
* not cached, so the next evaluation asks the registry again.
|
|
245
|
+
*/
|
|
246
|
+
const readExpressionFunctionTable = (registry) => {
|
|
247
|
+
const cached = tables.get(registry);
|
|
248
|
+
if (cached) {
|
|
249
|
+
return cached;
|
|
250
|
+
}
|
|
251
|
+
let functions;
|
|
252
|
+
try {
|
|
253
|
+
functions = Object.freeze(readListedNames(registry.list()).map((name) => readRegisteredFunction(registry.get(name), name)));
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
return failRegistry(error instanceof RegistryShapeError ? 'registry-shape' : 'registry-threw');
|
|
257
|
+
}
|
|
258
|
+
const table = Object.freeze({
|
|
259
|
+
functions,
|
|
260
|
+
names: new Set(functions.map((fn) => fn.name)),
|
|
261
|
+
});
|
|
262
|
+
tables.set(registry, table);
|
|
263
|
+
return table;
|
|
264
|
+
};
|
|
265
|
+
exports.readExpressionFunctionTable = readExpressionFunctionTable;
|
|
266
|
+
const BUILT_IN_NAMES = new Set(bpm_core_shared_1.BPM_BUILT_IN_EXPRESSION_FUNCTION_NAMES);
|
|
267
|
+
/**
|
|
268
|
+
* Whether a called name is one a registry decides: shaped like a host function
|
|
269
|
+
* (`…_vN`) or one of the nine built-ins. The engine's own functions (`size`,
|
|
270
|
+
* `int`, `matches`, …) never are, and neither is any other unknown name, which
|
|
271
|
+
* keeps the engine's own error (`EXPRESSION_TYPE_ERROR`) at evaluation and is
|
|
272
|
+
* refused outright by the publish lint (docs/24 §2.1, 2026-11-07).
|
|
273
|
+
*
|
|
274
|
+
* The publish lint, the rollback check and {@link callsUnavailableFunction}
|
|
275
|
+
* all read this one rule.
|
|
276
|
+
*/
|
|
277
|
+
const isRegistryFunctionName = (name) => BUILT_IN_NAMES.has(name) || bpm_core_shared_1.BPM_EXPRESSION_FUNCTION_NAME_PATTERN.test(name);
|
|
278
|
+
exports.isRegistryFunctionName = isRegistryFunctionName;
|
|
279
|
+
/**
|
|
280
|
+
* Whether an expression calls a function the registry does not have — a host
|
|
281
|
+
* function withdrawn after the version was published, or a built-in a custom
|
|
282
|
+
* registry left out. Only names {@link isRegistryFunctionName} accepts count:
|
|
283
|
+
* any other unknown name keeps the engine's own error, as before functions
|
|
284
|
+
* existed. The caller fails the evaluation with `EXPRESSION_FUNCTION_FAILED`
|
|
285
|
+
* before the engine runs, so the outcome does not depend on which branch the
|
|
286
|
+
* engine would have reached.
|
|
287
|
+
*/
|
|
288
|
+
const callsUnavailableFunction = (calledNames, table) => calledNames.some((name) => (0, exports.isRegistryFunctionName)(name) && !table.names.has(name));
|
|
289
|
+
exports.callsUnavailableFunction = callsUnavailableFunction;
|
|
290
|
+
//# sourceMappingURL=expression-function-invocation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expression-function-invocation.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/condition/expression-function-invocation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
|
|
2
|
+
readonly [key: string]: JsonValue;
|
|
3
|
+
};
|
|
4
|
+
export interface ExpressionResultLimits {
|
|
5
|
+
readonly maxDepth: number;
|
|
6
|
+
readonly maxNodes: number;
|
|
7
|
+
/** Upper bound on the size the value would have as JSON, estimated while walking. */
|
|
8
|
+
readonly maxBytes: number;
|
|
9
|
+
}
|
|
10
|
+
export declare const DEFAULT_EXPRESSION_RESULT_LIMITS: ExpressionResultLimits;
|
|
11
|
+
/**
|
|
12
|
+
* Turns whatever an engine returned into a fresh, deep-frozen JSON value, or
|
|
13
|
+
* throws `EXPRESSION_INVALID_RESULT`. Functions, promises, class instances,
|
|
14
|
+
* `undefined`, non-finite numbers and out-of-range integers are all refused. The
|
|
15
|
+
* size is estimated while walking, not with `JSON.stringify`, so a huge shared
|
|
16
|
+
* string cannot cost much before it is refused.
|
|
17
|
+
*/
|
|
18
|
+
export declare const normalizeExpressionResult: (raw: unknown, label: string, limits?: ExpressionResultLimits) => JsonValue;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeExpressionResult = exports.DEFAULT_EXPRESSION_RESULT_LIMITS = void 0;
|
|
4
|
+
const expression_errors_1 = require("./expression-errors");
|
|
5
|
+
exports.DEFAULT_EXPRESSION_RESULT_LIMITS = {
|
|
6
|
+
maxBytes: 64 * 1024,
|
|
7
|
+
maxDepth: 32,
|
|
8
|
+
maxNodes: 10_000,
|
|
9
|
+
};
|
|
10
|
+
// Writing these as object keys would let a result reach a prototype when a
|
|
11
|
+
// consumer merges it. Rejected at any depth where they are own keys of the
|
|
12
|
+
// value the engine returned. What reaches the walker depends on the engine
|
|
13
|
+
// (verified on both real engines 2026-11-07; differential corpus P43–P45):
|
|
14
|
+
// - cel-legacy (cel-js 0.8.2) builds a map literal by assignment, so
|
|
15
|
+
// `{"constructor": 1}` arrives with that own key and is refused here, and
|
|
16
|
+
// `{"__proto__": {...}}` swaps the prototype and is refused by
|
|
17
|
+
// `isPlainObject`.
|
|
18
|
+
// - cel-v2 (@marcbachmann/cel-js 8.0.0) drops all three keys while building
|
|
19
|
+
// the map, so `{"constructor": 1}` arrives as `{}` and nothing is refused:
|
|
20
|
+
// no pollution, but the key is lost silently instead of failing closed.
|
|
21
|
+
// Refusing it needs the AST, which is P2's (docs/24 §2.5).
|
|
22
|
+
const FORBIDDEN_KEYS = new Set([
|
|
23
|
+
'__proto__',
|
|
24
|
+
'constructor',
|
|
25
|
+
'prototype',
|
|
26
|
+
]);
|
|
27
|
+
const NUMBER_BYTES = 24;
|
|
28
|
+
const LITERAL_BYTES = 5;
|
|
29
|
+
const KEY_OVERHEAD_BYTES = 4;
|
|
30
|
+
/**
|
|
31
|
+
* A map the walker may copy: its prototype is `Object.prototype` or `null`, and
|
|
32
|
+
* it is neither `Object.prototype` itself nor an array. `Object.prototype` has a `null`
|
|
33
|
+
* prototype, so without the second check an engine that handed back the shared
|
|
34
|
+
* prototype would have it accepted as an empty map.
|
|
35
|
+
*/
|
|
36
|
+
const isPlainObject = (value) => {
|
|
37
|
+
// An array whose prototype was swapped for Object.prototype is still an
|
|
38
|
+
// array exotic object; it is refused, not copied as a map of indexes.
|
|
39
|
+
if (value === Object.prototype || Array.isArray(value)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const prototype = Object.getPrototypeOf(value);
|
|
43
|
+
return prototype === Object.prototype || prototype === null;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* A list the walker may copy: a real array whose prototype is
|
|
47
|
+
* `Array.prototype`. `Array.isArray` alone also accepts `Array.prototype`
|
|
48
|
+
* itself, arrays from another realm and arrays whose prototype was replaced.
|
|
49
|
+
*/
|
|
50
|
+
const isPlainArray = (value) => Array.isArray(value) &&
|
|
51
|
+
value !== Array.prototype &&
|
|
52
|
+
Object.getPrototypeOf(value) === Array.prototype;
|
|
53
|
+
/**
|
|
54
|
+
* Reads an own property without running code. An accessor is refused rather
|
|
55
|
+
* than called: a getter would run outside the time budget, and what it returns
|
|
56
|
+
* could differ from one read to the next.
|
|
57
|
+
*/
|
|
58
|
+
const readDataProperty = (value, key, refuse) => {
|
|
59
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
60
|
+
return descriptor && 'value' in descriptor ? descriptor.value : refuse();
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Turns whatever an engine returned into a fresh, deep-frozen JSON value, or
|
|
64
|
+
* throws `EXPRESSION_INVALID_RESULT`. Functions, promises, class instances,
|
|
65
|
+
* `undefined`, non-finite numbers and out-of-range integers are all refused. The
|
|
66
|
+
* size is estimated while walking, not with `JSON.stringify`, so a huge shared
|
|
67
|
+
* string cannot cost much before it is refused.
|
|
68
|
+
*/
|
|
69
|
+
const normalizeExpressionResult = (raw, label, limits = exports.DEFAULT_EXPRESSION_RESULT_LIMITS) => {
|
|
70
|
+
// Mutable on purpose: one ledger spans the whole walk, and returning the
|
|
71
|
+
// counts from every recursive call would make the walker harder to read.
|
|
72
|
+
const ledger = { bytes: 0, nodes: 0 };
|
|
73
|
+
const refuse = () => {
|
|
74
|
+
throw new expression_errors_1.ExpressionRuntimeError('EXPRESSION_INVALID_RESULT', label);
|
|
75
|
+
};
|
|
76
|
+
const charge = (bytes) => {
|
|
77
|
+
ledger.bytes += bytes;
|
|
78
|
+
if (ledger.bytes > limits.maxBytes) {
|
|
79
|
+
refuse();
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const walk = (value, depth) => {
|
|
83
|
+
if (depth > limits.maxDepth) {
|
|
84
|
+
refuse();
|
|
85
|
+
}
|
|
86
|
+
ledger.nodes += 1;
|
|
87
|
+
if (ledger.nodes > limits.maxNodes) {
|
|
88
|
+
refuse();
|
|
89
|
+
}
|
|
90
|
+
if (value === null) {
|
|
91
|
+
charge(LITERAL_BYTES);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
if (typeof value === 'boolean') {
|
|
95
|
+
charge(LITERAL_BYTES);
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
if (typeof value === 'number') {
|
|
99
|
+
// Every double beyond 2^53 is an integer, so this is exactly the out-of-
|
|
100
|
+
// safe-range case. cel-legacy returns numbers, not bigints, so this is the
|
|
101
|
+
// branch that actually sees an oversized integer result.
|
|
102
|
+
if (!Number.isFinite(value) ||
|
|
103
|
+
Math.abs(value) > Number.MAX_SAFE_INTEGER) {
|
|
104
|
+
refuse();
|
|
105
|
+
}
|
|
106
|
+
charge(NUMBER_BYTES);
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
if (typeof value === 'bigint') {
|
|
110
|
+
if (value > BigInt(Number.MAX_SAFE_INTEGER) ||
|
|
111
|
+
value < BigInt(Number.MIN_SAFE_INTEGER)) {
|
|
112
|
+
refuse();
|
|
113
|
+
}
|
|
114
|
+
charge(NUMBER_BYTES);
|
|
115
|
+
return Number(value);
|
|
116
|
+
}
|
|
117
|
+
if (typeof value === 'string') {
|
|
118
|
+
// UTF-8 never takes fewer bytes than UTF-16 code units, so a string longer
|
|
119
|
+
// than the whole allowance is refused before its bytes are counted.
|
|
120
|
+
if (value.length > limits.maxBytes - ledger.bytes) {
|
|
121
|
+
refuse();
|
|
122
|
+
}
|
|
123
|
+
charge(Buffer.byteLength(value, 'utf8'));
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
if (typeof value === 'object' && isPlainArray(value)) {
|
|
127
|
+
const list = value;
|
|
128
|
+
// Indexes are read as data properties, so a getter on an index (or a hole
|
|
129
|
+
// that would read through to the prototype) is refused, not run.
|
|
130
|
+
return Object.freeze(Array.from({ length: list.length }, (_item, index) => walk(readDataProperty(list, String(index), refuse), depth + 1)));
|
|
131
|
+
}
|
|
132
|
+
if (typeof value === 'object' && value !== null && isPlainObject(value)) {
|
|
133
|
+
// Symbol keys cannot be written as JSON; a value that carries one is not
|
|
134
|
+
// the plain map it looks like.
|
|
135
|
+
if (Object.getOwnPropertySymbols(value).length > 0) {
|
|
136
|
+
refuse();
|
|
137
|
+
}
|
|
138
|
+
const entries = Object.keys(value).map((key) => {
|
|
139
|
+
if (FORBIDDEN_KEYS.has(key)) {
|
|
140
|
+
refuse();
|
|
141
|
+
}
|
|
142
|
+
charge(Buffer.byteLength(key, 'utf8') + KEY_OVERHEAD_BYTES);
|
|
143
|
+
return [key, walk(readDataProperty(value, key, refuse), depth + 1)];
|
|
144
|
+
});
|
|
145
|
+
return Object.freeze(Object.fromEntries(entries));
|
|
146
|
+
}
|
|
147
|
+
return refuse();
|
|
148
|
+
};
|
|
149
|
+
return walk(raw, 0);
|
|
150
|
+
};
|
|
151
|
+
exports.normalizeExpressionResult = normalizeExpressionResult;
|
|
152
|
+
//# sourceMappingURL=expression-result.walker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expression-result.walker.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/condition/expression-result.walker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { ExpressionBudget } from './expression-budget';
|
|
2
|
+
import { type JsonValue } from './expression-result.walker';
|
|
3
|
+
import type { BPMExpressionFunctionRegistry } from '../expression-function/expression-function.types';
|
|
4
|
+
import { type ExpressionDialect } from './expression-dialect';
|
|
5
|
+
/** An engine's compiled form of one expression. `native` is never read outside its adapter. */
|
|
6
|
+
export interface ExpressionHandle {
|
|
7
|
+
readonly native: unknown;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* One engine behind the runtime. `compile` runs before any budget starts and
|
|
11
|
+
* returns `null` when the expression does not parse. `evaluate` runs inside the
|
|
12
|
+
* budget and may throw whatever the engine throws; the runtime maps it.
|
|
13
|
+
*/
|
|
14
|
+
export interface ExpressionEngineAdapter {
|
|
15
|
+
compile(expression: string): ExpressionHandle | null;
|
|
16
|
+
evaluate(handle: ExpressionHandle, roots: Readonly<Record<string, unknown>>): unknown;
|
|
17
|
+
}
|
|
18
|
+
export interface ExpressionEvalRequest {
|
|
19
|
+
readonly expression: string;
|
|
20
|
+
/** Names the use point in errors and logs. Must never contain a context value. */
|
|
21
|
+
readonly label: string;
|
|
22
|
+
/** Only the roots the use point allows. */
|
|
23
|
+
readonly roots: Readonly<Record<string, unknown>>;
|
|
24
|
+
readonly budget: ExpressionBudget;
|
|
25
|
+
/**
|
|
26
|
+
* The engine to evaluate with: the dialect of the definition or rule the
|
|
27
|
+
* expression belongs to. Absent means `cel-legacy` (ADR 22 §3.3).
|
|
28
|
+
*/
|
|
29
|
+
readonly dialect?: ExpressionDialect;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The single place where an expression is compiled and evaluated (ADR 22 §3.2).
|
|
33
|
+
* Every caller goes through here: nothing reaches an engine directly.
|
|
34
|
+
*
|
|
35
|
+
* Order: compile (outside any budget, so an interrupt cannot land in module
|
|
36
|
+
* loading), check the budget, evaluate under `runWithinTimeBudget`, normalize
|
|
37
|
+
* the result, and map any failure to an {@link ExpressionRuntimeError}.
|
|
38
|
+
*
|
|
39
|
+
* A timeout raises `EXPRESSION_BUDGET_EXCEEDED`. It never returns `false`: an
|
|
40
|
+
* expression that did not finish is not the same as one that is false.
|
|
41
|
+
*/
|
|
42
|
+
export declare class ExpressionRuntime {
|
|
43
|
+
private readonly legacyAdapter;
|
|
44
|
+
private readonly v2Adapter;
|
|
45
|
+
/**
|
|
46
|
+
* Without arguments the runtime calls the nine built-in functions only
|
|
47
|
+
* (ADR 22 §3.5); use {@link ExpressionRuntime.withFunctions} for a host's.
|
|
48
|
+
*/
|
|
49
|
+
constructor(legacyAdapter?: ExpressionEngineAdapter, v2Adapter?: ExpressionEngineAdapter);
|
|
50
|
+
/**
|
|
51
|
+
* The runtime for a function registry: both dialects call the functions it
|
|
52
|
+
* lists, through the same argument, result and failure rules. One runtime
|
|
53
|
+
* per registry, cached, so every caller shares its compiled environments.
|
|
54
|
+
* The built-ins-only registry gets the default adapters.
|
|
55
|
+
*/
|
|
56
|
+
static withFunctions(registry: BPMExpressionFunctionRegistry): ExpressionRuntime;
|
|
57
|
+
/**
|
|
58
|
+
* Compiles with the dialect's engine. This is also what loads that engine,
|
|
59
|
+
* so callers that compile before starting a budget never have an interrupt
|
|
60
|
+
* land inside module loading.
|
|
61
|
+
*/
|
|
62
|
+
compile(expression: string, label: string, dialect?: ExpressionDialect): ExpressionHandle;
|
|
63
|
+
evaluate(request: ExpressionEvalRequest): JsonValue;
|
|
64
|
+
private readAdapter;
|
|
65
|
+
/**
|
|
66
|
+
* Logs only the label, the dialect, the code and the elapsed time. The message, the key
|
|
67
|
+
* name and the expression are not logged: they can carry case data.
|
|
68
|
+
*/
|
|
69
|
+
private logFailure;
|
|
70
|
+
}
|