@object-ui/core 17.3.0 → 17.6.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/CHANGELOG.md +2018 -0
- package/README.md +38 -34
- package/dist/actions/ActionEngine.d.ts +43 -26
- package/dist/actions/ActionEngine.js +67 -54
- package/dist/actions/ActionRunner.d.ts +281 -31
- package/dist/actions/ActionRunner.js +458 -36
- package/dist/actions/actionKeys.d.ts +163 -27
- package/dist/actions/actionKeys.js +238 -27
- package/dist/actions/actionResponse.js +1 -1
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/actions/recordIdParam.d.ts +77 -0
- package/dist/actions/recordIdParam.js +37 -0
- package/dist/actions/serverActionHandler.d.ts +1 -1
- package/dist/actions/serverActionHandler.js +1 -1
- package/dist/data-scope/ViewDataProvider.d.ts +37 -12
- package/dist/data-scope/ViewDataProvider.js +83 -5
- package/dist/data-scope/element-data-source.d.ts +113 -0
- package/dist/data-scope/element-data-source.js +216 -0
- package/dist/data-scope/index.d.ts +2 -1
- package/dist/data-scope/index.js +1 -0
- package/dist/evaluator/ExpressionEvaluator.d.ts +42 -1
- package/dist/evaluator/ExpressionEvaluator.js +42 -1
- package/dist/evaluator/SafeExpressionParser.js +0 -4
- package/dist/evaluator/declaredPredicate.d.ts +119 -0
- package/dist/evaluator/declaredPredicate.js +152 -0
- package/dist/evaluator/fieldRules.d.ts +35 -0
- package/dist/evaluator/fieldRules.js +79 -20
- package/dist/evaluator/index.d.ts +2 -0
- package/dist/evaluator/index.js +2 -0
- package/dist/evaluator/listConditional.d.ts +54 -4
- package/dist/evaluator/listConditional.js +125 -19
- package/dist/evaluator/optionRules.d.ts +55 -0
- package/dist/evaluator/optionRules.js +60 -0
- package/dist/evaluator/predicateInput.d.ts +51 -3
- package/dist/evaluator/predicateInput.js +62 -5
- package/dist/evaluator/rowPredicateCanon.d.ts +67 -0
- package/dist/evaluator/rowPredicateCanon.js +208 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.js +31 -1
- package/dist/protocols/DndProtocol.d.ts +42 -1
- package/dist/protocols/KeyboardProtocol.d.ts +31 -1
- package/dist/protocols/SharingProtocol.d.ts +11 -1
- package/dist/registry/Registry.d.ts +62 -16
- package/dist/theme/ThemeEngine.d.ts +0 -8
- package/dist/theme/ThemeEngine.js +22 -103
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.js +7 -1
- package/dist/types/index.d.ts +125 -12
- package/dist/utils/chart-presentation.d.ts +205 -0
- package/dist/utils/chart-presentation.js +280 -0
- package/dist/utils/chart-series.d.ts +390 -23
- package/dist/utils/chart-series.js +776 -32
- package/dist/utils/column-identity.d.ts +43 -0
- package/dist/utils/column-identity.js +47 -1
- package/dist/utils/compare-to.d.ts +40 -13
- package/dist/utils/compare-to.js +4 -32
- package/dist/utils/dashboard-filters.d.ts +54 -8
- package/dist/utils/dashboard-filters.js +261 -12
- package/dist/utils/dataset-format.d.ts +73 -14
- package/dist/utils/dataset-format.js +142 -9
- package/dist/utils/dataset-pivot.d.ts +28 -7
- package/dist/utils/dataset-pivot.js +27 -6
- package/dist/utils/debug.js +7 -0
- package/dist/utils/dom-props.d.ts +137 -0
- package/dist/utils/dom-props.js +153 -0
- package/dist/utils/expand-fields.d.ts +97 -7
- package/dist/utils/expand-fields.js +97 -7
- package/dist/utils/filter-tokens.js +0 -1
- package/dist/utils/freeze-schema.d.ts +38 -1
- package/dist/utils/freeze-schema.js +10 -0
- package/dist/utils/humanize-label.d.ts +60 -0
- package/dist/utils/humanize-label.js +62 -0
- package/dist/utils/managedBy.d.ts +27 -2
- package/dist/utils/normalize-list-view.d.ts +36 -6
- package/dist/utils/normalize-list-view.js +93 -9
- package/dist/utils/number-display.d.ts +155 -0
- package/dist/utils/number-display.js +71 -0
- package/dist/utils/predicate-fields.d.ts +29 -4
- package/dist/utils/predicate-fields.js +44 -4
- package/dist/utils/resolve-view-id.d.ts +31 -0
- package/dist/utils/resolve-view-id.js +45 -0
- package/dist/utils/retired-field-types.d.ts +90 -0
- package/dist/utils/retired-field-types.js +124 -0
- package/dist/utils/sort-query.d.ts +57 -0
- package/dist/utils/sort-query.js +35 -0
- package/dist/utils/unmaterialized-fields.d.ts +52 -0
- package/dist/utils/unmaterialized-fields.js +57 -0
- package/dist/validation/index.d.ts +1 -0
- package/dist/validation/index.js +1 -0
- package/dist/validation/schema-validator.js +105 -0
- package/dist/validation/server-owned-value.d.ts +62 -0
- package/dist/validation/server-owned-value.js +99 -0
- package/dist/validation/validators/object-validation-engine.js +0 -2
- package/package.json +5 -5
- package/dist/__benchmarks__/core.bench.d.ts +0 -8
- package/dist/__benchmarks__/core.bench.js +0 -53
- package/dist/utils/merge-views-into-objects.d.ts +0 -19
- package/dist/utils/merge-views-into-objects.js +0 -39
|
@@ -129,7 +129,48 @@ export declare class ExpressionEvaluator {
|
|
|
129
129
|
*/
|
|
130
130
|
getFormulas(): FormulaFunctions;
|
|
131
131
|
/**
|
|
132
|
-
* Register a custom formula function
|
|
132
|
+
* Register a custom formula function.
|
|
133
|
+
*
|
|
134
|
+
* **The name is case-folded: it is stored — and must be called — in UPPER
|
|
135
|
+
* CASE.** `registerFunction('formatCurrency', fn)` registers
|
|
136
|
+
* `FORMATCURRENCY`, and an expression has to spell it that way:
|
|
137
|
+
* `${FORMATCURRENCY(price)}` resolves, `${formatCurrency(price)}` does not.
|
|
138
|
+
* The fold is deliberate — formula names are spreadsheet-style vocabulary
|
|
139
|
+
* (`SUM`, `IF`, `UPPER`) and the built-ins go through the very same
|
|
140
|
+
* {@link FormulaFunctions.register} — but two things keep it from being
|
|
141
|
+
* self-evident at the call site:
|
|
142
|
+
*
|
|
143
|
+
* 1. The registry API stays case-insensitive, so it never surfaces the fold:
|
|
144
|
+
* `getFormulas().has('formatCurrency')` is `true` and `.get()` returns the
|
|
145
|
+
* function. Only expressions see the stored spelling, because the
|
|
146
|
+
* evaluation scope is built from `FormulaFunctions.toObject()` — a plain
|
|
147
|
+
* object, whose identifiers expressions match case-sensitively.
|
|
148
|
+
* 2. A wrong-case call site does not raise. {@link evaluate} catches, warns,
|
|
149
|
+
* and returns `defaultValue ?? expression`, so the template renders its own
|
|
150
|
+
* `${...}` source as literal text.
|
|
151
|
+
* {@link ExpressionEvaluator.evaluateExpression} is the throwing sibling,
|
|
152
|
+
* and reports `'formatCurrency' is not a function`.
|
|
153
|
+
*
|
|
154
|
+
* To keep a name's exact spelling, supply the function as evaluation context
|
|
155
|
+
* data instead: context entries are merged over the formulas and stay verbatim.
|
|
156
|
+
*
|
|
157
|
+
* @param name Function name; folded to upper case for both storage and lookup.
|
|
158
|
+
* @param fn Implementation, invoked with the evaluated call arguments.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* const evaluator = new ExpressionEvaluator({ price: 1234.5 });
|
|
163
|
+
* evaluator.registerFunction('formatCurrency', fmt);
|
|
164
|
+
*
|
|
165
|
+
* evaluator.evaluate('${FORMATCURRENCY(price)}'); // '$1,234.50'
|
|
166
|
+
* evaluator.evaluate('${formatCurrency(price)}'); // '${formatCurrency(price)}' — the source, verbatim
|
|
167
|
+
*
|
|
168
|
+
* // Case-sensitive alternative — the function travels as context data.
|
|
169
|
+
* // Note the receiver: the call below is the MODULE-LEVEL export (a context
|
|
170
|
+
* // bag as its second parameter), not the method used above.
|
|
171
|
+
* import { evaluateExpression } from '@object-ui/core';
|
|
172
|
+
* evaluateExpression('${formatCurrency(price)}', { formatCurrency: fmt, price: 1234.5 }); // '$1,234.50'
|
|
173
|
+
* ```
|
|
133
174
|
*/
|
|
134
175
|
registerFunction(name: string, fn: (...args: any[]) => any): void;
|
|
135
176
|
}
|
|
@@ -305,7 +305,48 @@ export class ExpressionEvaluator {
|
|
|
305
305
|
return this.formulas;
|
|
306
306
|
}
|
|
307
307
|
/**
|
|
308
|
-
* Register a custom formula function
|
|
308
|
+
* Register a custom formula function.
|
|
309
|
+
*
|
|
310
|
+
* **The name is case-folded: it is stored — and must be called — in UPPER
|
|
311
|
+
* CASE.** `registerFunction('formatCurrency', fn)` registers
|
|
312
|
+
* `FORMATCURRENCY`, and an expression has to spell it that way:
|
|
313
|
+
* `${FORMATCURRENCY(price)}` resolves, `${formatCurrency(price)}` does not.
|
|
314
|
+
* The fold is deliberate — formula names are spreadsheet-style vocabulary
|
|
315
|
+
* (`SUM`, `IF`, `UPPER`) and the built-ins go through the very same
|
|
316
|
+
* {@link FormulaFunctions.register} — but two things keep it from being
|
|
317
|
+
* self-evident at the call site:
|
|
318
|
+
*
|
|
319
|
+
* 1. The registry API stays case-insensitive, so it never surfaces the fold:
|
|
320
|
+
* `getFormulas().has('formatCurrency')` is `true` and `.get()` returns the
|
|
321
|
+
* function. Only expressions see the stored spelling, because the
|
|
322
|
+
* evaluation scope is built from `FormulaFunctions.toObject()` — a plain
|
|
323
|
+
* object, whose identifiers expressions match case-sensitively.
|
|
324
|
+
* 2. A wrong-case call site does not raise. {@link evaluate} catches, warns,
|
|
325
|
+
* and returns `defaultValue ?? expression`, so the template renders its own
|
|
326
|
+
* `${...}` source as literal text.
|
|
327
|
+
* {@link ExpressionEvaluator.evaluateExpression} is the throwing sibling,
|
|
328
|
+
* and reports `'formatCurrency' is not a function`.
|
|
329
|
+
*
|
|
330
|
+
* To keep a name's exact spelling, supply the function as evaluation context
|
|
331
|
+
* data instead: context entries are merged over the formulas and stay verbatim.
|
|
332
|
+
*
|
|
333
|
+
* @param name Function name; folded to upper case for both storage and lookup.
|
|
334
|
+
* @param fn Implementation, invoked with the evaluated call arguments.
|
|
335
|
+
*
|
|
336
|
+
* @example
|
|
337
|
+
* ```ts
|
|
338
|
+
* const evaluator = new ExpressionEvaluator({ price: 1234.5 });
|
|
339
|
+
* evaluator.registerFunction('formatCurrency', fmt);
|
|
340
|
+
*
|
|
341
|
+
* evaluator.evaluate('${FORMATCURRENCY(price)}'); // '$1,234.50'
|
|
342
|
+
* evaluator.evaluate('${formatCurrency(price)}'); // '${formatCurrency(price)}' — the source, verbatim
|
|
343
|
+
*
|
|
344
|
+
* // Case-sensitive alternative — the function travels as context data.
|
|
345
|
+
* // Note the receiver: the call below is the MODULE-LEVEL export (a context
|
|
346
|
+
* // bag as its second parameter), not the method used above.
|
|
347
|
+
* import { evaluateExpression } from '@object-ui/core';
|
|
348
|
+
* evaluateExpression('${formatCurrency(price)}', { formatCurrency: fmt, price: 1234.5 }); // '$1,234.50'
|
|
349
|
+
* ```
|
|
309
350
|
*/
|
|
310
351
|
registerFunction(name, fn) {
|
|
311
352
|
this.formulas.register(name, fn);
|
|
@@ -296,7 +296,6 @@ export class SafeExpressionParser {
|
|
|
296
296
|
parseEquality() {
|
|
297
297
|
let left = this.parseAddition();
|
|
298
298
|
this.skipWhitespace();
|
|
299
|
-
// eslint-disable-next-line no-constant-condition
|
|
300
299
|
while (true) {
|
|
301
300
|
// Every branch below either assigns `op` or `break`s out of the loop, so
|
|
302
301
|
// `op` is always set by the time the switch reads it (no null initializer
|
|
@@ -346,11 +345,9 @@ export class SafeExpressionParser {
|
|
|
346
345
|
case '!==':
|
|
347
346
|
left = left !== right;
|
|
348
347
|
break;
|
|
349
|
-
// eslint-disable-next-line eqeqeq
|
|
350
348
|
case '==':
|
|
351
349
|
left = left == right;
|
|
352
350
|
break;
|
|
353
|
-
// eslint-disable-next-line eqeqeq
|
|
354
351
|
case '!=':
|
|
355
352
|
left = left != right;
|
|
356
353
|
break;
|
|
@@ -438,7 +435,6 @@ export class SafeExpressionParser {
|
|
|
438
435
|
/** Level 9 — Member access, method calls, function calls */
|
|
439
436
|
parseMember() {
|
|
440
437
|
let obj = this.parsePrimary();
|
|
441
|
-
// eslint-disable-next-line no-constant-condition
|
|
442
438
|
while (true) {
|
|
443
439
|
this.skipWhitespace();
|
|
444
440
|
const isOptionalDot = this.peek() === '?' && this.peek(1) === '.';
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Is a predicate gate DECLARED on this value — i.e. after normalization, is
|
|
10
|
+
* there still a CONDITION for {@link ExpressionEvaluator.evaluateCondition} to
|
|
11
|
+
* reach a verdict on? (objectui#3850's ruling, key-neutral: `visible` /
|
|
12
|
+
* `hidden` / `enabled` / `disabled` / `condition` all ask it.)
|
|
13
|
+
*
|
|
14
|
+
* This is the ONE definition of that question in the repo, and it lives here —
|
|
15
|
+
* one layer under every consumer, beside {@link toPredicateInput}, whose answer
|
|
16
|
+
* it is derived from. It has to be asked separately from the verdict because
|
|
17
|
+
* `evaluateCondition` documents and implements exactly one default for "there
|
|
18
|
+
* is nothing here to evaluate": it returns `true`, meaning *visible/enabled*.
|
|
19
|
+
* That default is right on `visible` / `enabled` and INVERTED on `disabled`,
|
|
20
|
+
* where `true` means "greyed out" — so a gate that hands an empty predicate to
|
|
21
|
+
* the evaluator gets the strongest possible "yes, disable it" for a value the
|
|
22
|
+
* metadata never used to say anything. Truthiness cannot answer it either, and
|
|
23
|
+
* asking it that way was objectui#3812: `if (action.visible && !isVisible)`
|
|
24
|
+
* read `visible: false` — the most explicit "never show this" an author can
|
|
25
|
+
* write — as *ungated*, and rendered the action for everyone.
|
|
26
|
+
*
|
|
27
|
+
* ## What counts as "nothing to evaluate"
|
|
28
|
+
*
|
|
29
|
+
* Exactly the shapes {@link toPredicateInput} folds to `undefined`, plus the two
|
|
30
|
+
* BLANK spellings it wraps or passes through instead of folding:
|
|
31
|
+
*
|
|
32
|
+
* - `null` / `undefined` — no key at all.
|
|
33
|
+
* - `''` — an empty predicate (objectui#3492 / objectui#3842).
|
|
34
|
+
* - blank predicate TEXT, in either spelling — a whitespace-only string
|
|
35
|
+
* (`' '`, which the normalizer wraps into `'${ }'`) and an envelope whose
|
|
36
|
+
* `source` is whitespace-only (`{ dialect: 'cel', source: ' ' }`, which the
|
|
37
|
+
* normalizer passes through because it only folds a `source` of `''`).
|
|
38
|
+
* Neither can be read off the normalizer's answer, so both are named here —
|
|
39
|
+
* see {@link isBlankPredicateText} for why this is the layer that says it.
|
|
40
|
+
* Core's other predicate entries already treat both as blank:
|
|
41
|
+
* `evaluateCondition` (`if (!trimmed) return true`), `evaluateCelCondition`
|
|
42
|
+
* (`if (!source.trim()) return true`) and `evalRowPredicate`
|
|
43
|
+
* (`evaluator/listConditional.ts`, `if (!source.trim())`).
|
|
44
|
+
* - `{ dialect, source: '' }` — the empty ENVELOPE. This is not an exotic
|
|
45
|
+
* spelling: `@objectstack/spec`'s `ExpressionInputSchema` normalizes every
|
|
46
|
+
* authored predicate into an envelope, so "author left the predicate empty"
|
|
47
|
+
* compiles to exactly this, which makes it the likeliest empty shape in real
|
|
48
|
+
* metadata (objectui#3850).
|
|
49
|
+
* - anything that is not a predicate at all (`0`, `{}`, an array): a value the
|
|
50
|
+
* evaluator cannot read must not be the reason a control is disabled or an
|
|
51
|
+
* action refuses to run. Fail-open on junk, which is the posture
|
|
52
|
+
* `ActionRunner` already committed to (`catch { isDisabled = false }`).
|
|
53
|
+
*
|
|
54
|
+
* A declared-and-`false` gate is DECLARED (`toPredicateInput` returns the
|
|
55
|
+
* boolean unchanged): `disabled: false` / `visible: false` are verdicts, and
|
|
56
|
+
* routing them to the evaluator — which short-circuits booleans — is the point
|
|
57
|
+
* of asking "declared?" rather than "truthy?".
|
|
58
|
+
*
|
|
59
|
+
* ## Why blankness is decided HERE and not in the normalizer (objectui#3960)
|
|
60
|
+
*
|
|
61
|
+
* The blank-`source` envelope arrived one release late: objectui#3850's ruling
|
|
62
|
+
* enumerated three empty spellings, `toPredicateInput` folds a `source` of `''`
|
|
63
|
+
* and does not trim, so `{ dialect: 'cel', source: ' ' }` stayed "declared"
|
|
64
|
+
* while core's own CEL entry called that exact value "no predicate" — `disabled`
|
|
65
|
+
* greyed out and `ActionRunner` refused to execute for a predicate that says
|
|
66
|
+
* nothing (objectui#3960). The asymmetry was that this definition trimmed the
|
|
67
|
+
* STRING spelling of a blank predicate and not the ENVELOPE spelling of the same
|
|
68
|
+
* blank.
|
|
69
|
+
*
|
|
70
|
+
* Fixing it at the normalizer instead (`if (!src.trim()) return undefined`) would
|
|
71
|
+
* have aligned every `evaluateCondition(toPredicateInput(x))` consumer at once,
|
|
72
|
+
* and that is precisely why it is the wider, wrong lever: it changes what
|
|
73
|
+
* normalization MEANS, which is a shape question ("what does the evaluator
|
|
74
|
+
* accept"), to answer a declaredness question ("is there a condition"). Those are
|
|
75
|
+
* different concepts on purpose — a blank `source` is a shape the evaluator
|
|
76
|
+
* accepts and answers `true` for, by its own documented rule. The blast radius is
|
|
77
|
+
* the concrete difference: at the normalizer, a NON-cel blank envelope
|
|
78
|
+
* (`{ source: ' ' }` → `'${ }'` → falsy) flips verdict for every
|
|
79
|
+
* `useCondition(toPredicateInput(…))` call site including the container-level
|
|
80
|
+
* `visible` reads in `action-bar` / `action-group` / `action-menu` /
|
|
81
|
+
* `RelatedList` / `record-alert`, none of which ask this question at all; here it
|
|
82
|
+
* flips only for the consumers that DO ask it, and only on the inverted-polarity
|
|
83
|
+
* keys where "declared" is what turns a control off.
|
|
84
|
+
*
|
|
85
|
+
* ## Why the callers still evaluate the RAW value
|
|
86
|
+
*
|
|
87
|
+
* Every consumer normalizes to decide DECLAREDNESS and then evaluates whatever
|
|
88
|
+
* it was given, `evaluateCondition(raw)` or `evaluateCondition(toPredicateInput(
|
|
89
|
+
* raw))` as it did before. Both agree since objectui#3871 made the normalizer
|
|
90
|
+
* idempotent for an already-`${…}` string; before that they did not, and a
|
|
91
|
+
* template-spelled predicate normalized twice came back as a constant. Nothing
|
|
92
|
+
* here changes a verdict for a value that HAS one — this gate only decides
|
|
93
|
+
* whether there is one to reach.
|
|
94
|
+
*
|
|
95
|
+
* ## Scope history (objectui#3850)
|
|
96
|
+
*
|
|
97
|
+
* The question used to be answered three times with three different scopes: the
|
|
98
|
+
* renderer-side `hasDeclaredVisibilityGate` (`!= null && !== ''`, so every
|
|
99
|
+
* object counted — including the empty envelope), `SchemaRenderer`'s inline
|
|
100
|
+
* `!== undefined` (wider still: `disabled: null` greyed the control out,
|
|
101
|
+
* objectui#3862), and `ActionRunner`'s module-private helper (this scope, which
|
|
102
|
+
* arrived first with objectui#3848 and is what the ruling adopted). One
|
|
103
|
+
* definition with one scope replaces all three:
|
|
104
|
+
* `components/renderers/action/visibility-gate.ts` re-exports this function
|
|
105
|
+
* under its historic name `hasDeclaredVisibilityGate` (the five member-action
|
|
106
|
+
* renderer call sites are unchanged), `SchemaRenderer`'s `disabled` /
|
|
107
|
+
* `disabledOn` chain reads it, and `ActionRunner`'s two gates read it instead of
|
|
108
|
+
* a private twin.
|
|
109
|
+
*
|
|
110
|
+
* Two more consumers joined afterwards, and with them the last two places that
|
|
111
|
+
* answered this question with a range of their own:
|
|
112
|
+
* `SchemaRenderer`'s `hidden` / `hiddenOn` legs, whose verdict is NOT negated so
|
|
113
|
+
* an empty predicate made the node VANISH (objectui#3955), and
|
|
114
|
+
* `ActionEngine.getActionsForLocation`'s `visible` filter, which folded some
|
|
115
|
+
* empty spellings by hand and coerced the rest with `Boolean(raw)`, so
|
|
116
|
+
* `visible: 0` hid an action the renderers showed (objectui#3957). Nothing in the
|
|
117
|
+
* repo now asks "is a gate declared?" anywhere but here.
|
|
118
|
+
*/
|
|
119
|
+
export declare function hasDeclaredPredicate(value: unknown): boolean;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import { toPredicateInput } from './predicateInput.js';
|
|
9
|
+
/**
|
|
10
|
+
* Is the predicate TEXT blank — in EITHER spelling? (objectui#3960)
|
|
11
|
+
*
|
|
12
|
+
* `' '` and `{ dialect: 'cel', source: ' ' }` are the same author mistake
|
|
13
|
+
* written two ways, and {@link toPredicateInput} folds neither: it wraps a
|
|
14
|
+
* whitespace-only string into `'${ }'`, and it only folds an envelope whose
|
|
15
|
+
* `source` is exactly `''`. So blankness cannot be derived from the normalizer's
|
|
16
|
+
* answer and has to be stated once, here, for both spellings — trimming one and
|
|
17
|
+
* not the other is the asymmetry objectui#3960 measured (`disabled` greyed out,
|
|
18
|
+
* `ActionRunner` refused to execute, for a predicate that says nothing).
|
|
19
|
+
*
|
|
20
|
+
* This is not a fourth dialect of "empty": it is the SAME rule core's evaluation
|
|
21
|
+
* entries already apply on the value side — `evaluateCondition`
|
|
22
|
+
* (`if (!trimmed) return true`), `evaluateCelCondition` (`if (!source.trim())
|
|
23
|
+
* return true`), `evalRowPredicate` (`listConditional.ts`) — brought to the one
|
|
24
|
+
* place that answers "is there a condition at all?", so the two halves cannot
|
|
25
|
+
* disagree about the same blank.
|
|
26
|
+
*/
|
|
27
|
+
function isBlankPredicateText(value) {
|
|
28
|
+
if (typeof value === 'string')
|
|
29
|
+
return value.trim() === '';
|
|
30
|
+
if (value !== null && typeof value === 'object') {
|
|
31
|
+
const source = value.source;
|
|
32
|
+
if (typeof source === 'string')
|
|
33
|
+
return source.trim() === '';
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Is a predicate gate DECLARED on this value — i.e. after normalization, is
|
|
39
|
+
* there still a CONDITION for {@link ExpressionEvaluator.evaluateCondition} to
|
|
40
|
+
* reach a verdict on? (objectui#3850's ruling, key-neutral: `visible` /
|
|
41
|
+
* `hidden` / `enabled` / `disabled` / `condition` all ask it.)
|
|
42
|
+
*
|
|
43
|
+
* This is the ONE definition of that question in the repo, and it lives here —
|
|
44
|
+
* one layer under every consumer, beside {@link toPredicateInput}, whose answer
|
|
45
|
+
* it is derived from. It has to be asked separately from the verdict because
|
|
46
|
+
* `evaluateCondition` documents and implements exactly one default for "there
|
|
47
|
+
* is nothing here to evaluate": it returns `true`, meaning *visible/enabled*.
|
|
48
|
+
* That default is right on `visible` / `enabled` and INVERTED on `disabled`,
|
|
49
|
+
* where `true` means "greyed out" — so a gate that hands an empty predicate to
|
|
50
|
+
* the evaluator gets the strongest possible "yes, disable it" for a value the
|
|
51
|
+
* metadata never used to say anything. Truthiness cannot answer it either, and
|
|
52
|
+
* asking it that way was objectui#3812: `if (action.visible && !isVisible)`
|
|
53
|
+
* read `visible: false` — the most explicit "never show this" an author can
|
|
54
|
+
* write — as *ungated*, and rendered the action for everyone.
|
|
55
|
+
*
|
|
56
|
+
* ## What counts as "nothing to evaluate"
|
|
57
|
+
*
|
|
58
|
+
* Exactly the shapes {@link toPredicateInput} folds to `undefined`, plus the two
|
|
59
|
+
* BLANK spellings it wraps or passes through instead of folding:
|
|
60
|
+
*
|
|
61
|
+
* - `null` / `undefined` — no key at all.
|
|
62
|
+
* - `''` — an empty predicate (objectui#3492 / objectui#3842).
|
|
63
|
+
* - blank predicate TEXT, in either spelling — a whitespace-only string
|
|
64
|
+
* (`' '`, which the normalizer wraps into `'${ }'`) and an envelope whose
|
|
65
|
+
* `source` is whitespace-only (`{ dialect: 'cel', source: ' ' }`, which the
|
|
66
|
+
* normalizer passes through because it only folds a `source` of `''`).
|
|
67
|
+
* Neither can be read off the normalizer's answer, so both are named here —
|
|
68
|
+
* see {@link isBlankPredicateText} for why this is the layer that says it.
|
|
69
|
+
* Core's other predicate entries already treat both as blank:
|
|
70
|
+
* `evaluateCondition` (`if (!trimmed) return true`), `evaluateCelCondition`
|
|
71
|
+
* (`if (!source.trim()) return true`) and `evalRowPredicate`
|
|
72
|
+
* (`evaluator/listConditional.ts`, `if (!source.trim())`).
|
|
73
|
+
* - `{ dialect, source: '' }` — the empty ENVELOPE. This is not an exotic
|
|
74
|
+
* spelling: `@objectstack/spec`'s `ExpressionInputSchema` normalizes every
|
|
75
|
+
* authored predicate into an envelope, so "author left the predicate empty"
|
|
76
|
+
* compiles to exactly this, which makes it the likeliest empty shape in real
|
|
77
|
+
* metadata (objectui#3850).
|
|
78
|
+
* - anything that is not a predicate at all (`0`, `{}`, an array): a value the
|
|
79
|
+
* evaluator cannot read must not be the reason a control is disabled or an
|
|
80
|
+
* action refuses to run. Fail-open on junk, which is the posture
|
|
81
|
+
* `ActionRunner` already committed to (`catch { isDisabled = false }`).
|
|
82
|
+
*
|
|
83
|
+
* A declared-and-`false` gate is DECLARED (`toPredicateInput` returns the
|
|
84
|
+
* boolean unchanged): `disabled: false` / `visible: false` are verdicts, and
|
|
85
|
+
* routing them to the evaluator — which short-circuits booleans — is the point
|
|
86
|
+
* of asking "declared?" rather than "truthy?".
|
|
87
|
+
*
|
|
88
|
+
* ## Why blankness is decided HERE and not in the normalizer (objectui#3960)
|
|
89
|
+
*
|
|
90
|
+
* The blank-`source` envelope arrived one release late: objectui#3850's ruling
|
|
91
|
+
* enumerated three empty spellings, `toPredicateInput` folds a `source` of `''`
|
|
92
|
+
* and does not trim, so `{ dialect: 'cel', source: ' ' }` stayed "declared"
|
|
93
|
+
* while core's own CEL entry called that exact value "no predicate" — `disabled`
|
|
94
|
+
* greyed out and `ActionRunner` refused to execute for a predicate that says
|
|
95
|
+
* nothing (objectui#3960). The asymmetry was that this definition trimmed the
|
|
96
|
+
* STRING spelling of a blank predicate and not the ENVELOPE spelling of the same
|
|
97
|
+
* blank.
|
|
98
|
+
*
|
|
99
|
+
* Fixing it at the normalizer instead (`if (!src.trim()) return undefined`) would
|
|
100
|
+
* have aligned every `evaluateCondition(toPredicateInput(x))` consumer at once,
|
|
101
|
+
* and that is precisely why it is the wider, wrong lever: it changes what
|
|
102
|
+
* normalization MEANS, which is a shape question ("what does the evaluator
|
|
103
|
+
* accept"), to answer a declaredness question ("is there a condition"). Those are
|
|
104
|
+
* different concepts on purpose — a blank `source` is a shape the evaluator
|
|
105
|
+
* accepts and answers `true` for, by its own documented rule. The blast radius is
|
|
106
|
+
* the concrete difference: at the normalizer, a NON-cel blank envelope
|
|
107
|
+
* (`{ source: ' ' }` → `'${ }'` → falsy) flips verdict for every
|
|
108
|
+
* `useCondition(toPredicateInput(…))` call site including the container-level
|
|
109
|
+
* `visible` reads in `action-bar` / `action-group` / `action-menu` /
|
|
110
|
+
* `RelatedList` / `record-alert`, none of which ask this question at all; here it
|
|
111
|
+
* flips only for the consumers that DO ask it, and only on the inverted-polarity
|
|
112
|
+
* keys where "declared" is what turns a control off.
|
|
113
|
+
*
|
|
114
|
+
* ## Why the callers still evaluate the RAW value
|
|
115
|
+
*
|
|
116
|
+
* Every consumer normalizes to decide DECLAREDNESS and then evaluates whatever
|
|
117
|
+
* it was given, `evaluateCondition(raw)` or `evaluateCondition(toPredicateInput(
|
|
118
|
+
* raw))` as it did before. Both agree since objectui#3871 made the normalizer
|
|
119
|
+
* idempotent for an already-`${…}` string; before that they did not, and a
|
|
120
|
+
* template-spelled predicate normalized twice came back as a constant. Nothing
|
|
121
|
+
* here changes a verdict for a value that HAS one — this gate only decides
|
|
122
|
+
* whether there is one to reach.
|
|
123
|
+
*
|
|
124
|
+
* ## Scope history (objectui#3850)
|
|
125
|
+
*
|
|
126
|
+
* The question used to be answered three times with three different scopes: the
|
|
127
|
+
* renderer-side `hasDeclaredVisibilityGate` (`!= null && !== ''`, so every
|
|
128
|
+
* object counted — including the empty envelope), `SchemaRenderer`'s inline
|
|
129
|
+
* `!== undefined` (wider still: `disabled: null` greyed the control out,
|
|
130
|
+
* objectui#3862), and `ActionRunner`'s module-private helper (this scope, which
|
|
131
|
+
* arrived first with objectui#3848 and is what the ruling adopted). One
|
|
132
|
+
* definition with one scope replaces all three:
|
|
133
|
+
* `components/renderers/action/visibility-gate.ts` re-exports this function
|
|
134
|
+
* under its historic name `hasDeclaredVisibilityGate` (the five member-action
|
|
135
|
+
* renderer call sites are unchanged), `SchemaRenderer`'s `disabled` /
|
|
136
|
+
* `disabledOn` chain reads it, and `ActionRunner`'s two gates read it instead of
|
|
137
|
+
* a private twin.
|
|
138
|
+
*
|
|
139
|
+
* Two more consumers joined afterwards, and with them the last two places that
|
|
140
|
+
* answered this question with a range of their own:
|
|
141
|
+
* `SchemaRenderer`'s `hidden` / `hiddenOn` legs, whose verdict is NOT negated so
|
|
142
|
+
* an empty predicate made the node VANISH (objectui#3955), and
|
|
143
|
+
* `ActionEngine.getActionsForLocation`'s `visible` filter, which folded some
|
|
144
|
+
* empty spellings by hand and coerced the rest with `Boolean(raw)`, so
|
|
145
|
+
* `visible: 0` hid an action the renderers showed (objectui#3957). Nothing in the
|
|
146
|
+
* repo now asks "is a gate declared?" anywhere but here.
|
|
147
|
+
*/
|
|
148
|
+
export function hasDeclaredPredicate(value) {
|
|
149
|
+
if (isBlankPredicateText(value))
|
|
150
|
+
return false;
|
|
151
|
+
return toPredicateInput(value) !== undefined;
|
|
152
|
+
}
|
|
@@ -24,6 +24,22 @@ export interface FieldPredicateDiagnostic {
|
|
|
24
24
|
* warnings for one broken predicate. Defaults to `true`.
|
|
25
25
|
*/
|
|
26
26
|
warn?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Called with the engine's failure reason (`"[kind] message"`, the exact text
|
|
29
|
+
* the built-in warning prints after `Reason:`) when the predicate cannot be
|
|
30
|
+
* evaluated. This is the passback for callers that set `warn: false` and
|
|
31
|
+
* report the fault themselves — without it, silencing the built-in warning
|
|
32
|
+
* also discards the only description of *why* the predicate failed, which is
|
|
33
|
+
* how the fail-closed row-predicate path ended up the least debuggable one
|
|
34
|
+
* (objectui#3792).
|
|
35
|
+
*
|
|
36
|
+
* Independent of `warn` and of the one-time-warning dedupe: it fires on every
|
|
37
|
+
* fault, so a caller doing its own warn-once bookkeeping keeps control of it.
|
|
38
|
+
* The returned verdict is unaffected. Must not throw — it is invoked outside
|
|
39
|
+
* the engine guard, so an exception here propagates to the caller rather than
|
|
40
|
+
* being reported as an engine fault.
|
|
41
|
+
*/
|
|
42
|
+
onFault?: (reason: string) => void;
|
|
27
43
|
}
|
|
28
44
|
/**
|
|
29
45
|
* Evaluate a field-rule CEL predicate against a record.
|
|
@@ -52,6 +68,24 @@ export declare function evalFieldPredicate(pred: FieldRulePredicate | undefined
|
|
|
52
68
|
* `false` predicate result for required/readonly — but `visibleWhen` is
|
|
53
69
|
* authoritative when present (so a field can be conditionally shown/hidden).
|
|
54
70
|
*
|
|
71
|
+
* ## `statics.serverOwnedValue` — the one thing that outranks BOTH
|
|
72
|
+
*
|
|
73
|
+
* A CREATE form leaves a producer-owned control empty on purpose and omits the
|
|
74
|
+
* key, so the server resolves the declared runtime default (#4069). Required-
|
|
75
|
+
* ness is then not the client's question at all, and this is the single place
|
|
76
|
+
* that answers it: `required` resolves to `false` whatever `statics.required`
|
|
77
|
+
* says and whatever `requiredWhen` evaluates to.
|
|
78
|
+
*
|
|
79
|
+
* Both spellings had to land HERE, not in a gate a layer up. Since
|
|
80
|
+
* objectui#4201 the submit-time check and the required marker read one live
|
|
81
|
+
* verdict — this function's — so a suppression applied anywhere else would
|
|
82
|
+
* show a field without an asterisk and still refuse the submit, which is the
|
|
83
|
+
* exact two-layer disagreement #4201 removed. #4085 is the conditional half:
|
|
84
|
+
* `requiredWhen` resolving TRUE on a create form re-required a control the
|
|
85
|
+
* renderer deliberately left empty, with nothing the user could type to
|
|
86
|
+
* unblock it. See `isServerOwnedValue` in `../validation/server-owned-value.js`
|
|
87
|
+
* for who computes the fact and why the mode is the caller's to state.
|
|
88
|
+
*
|
|
55
89
|
* @param fieldContext Optional locator for failure warnings — e.g.
|
|
56
90
|
* `"field 'amount'"`. The warning then reads
|
|
57
91
|
* `visibleWhen of field 'amount' …`; without it, the rule
|
|
@@ -64,6 +98,7 @@ export declare function resolveFieldRuleState(rules: {
|
|
|
64
98
|
}, record: Record<string, unknown>, statics: {
|
|
65
99
|
required?: boolean;
|
|
66
100
|
readonly?: boolean;
|
|
101
|
+
serverOwnedValue?: boolean;
|
|
67
102
|
}, previous?: Record<string, unknown>, scope?: Record<string, unknown>, fieldContext?: string): {
|
|
68
103
|
visible: boolean;
|
|
69
104
|
readonly: boolean;
|
|
@@ -22,8 +22,32 @@
|
|
|
22
22
|
* CEL (mirrors the server's `toExpression`). Evaluation is *fail-open* for
|
|
23
23
|
* visibility/required (a broken predicate must not hide a field or wrongly
|
|
24
24
|
* block submit) and *fail-open* for readonly (a broken predicate leaves the
|
|
25
|
-
* field editable)
|
|
26
|
-
*
|
|
25
|
+
* field editable).
|
|
26
|
+
*
|
|
27
|
+
* Fail-open agrees with the server for MOST faults — but not for all, and the
|
|
28
|
+
* exception is the half worth knowing. Since objectstack#4889 a `readonlyWhen`
|
|
29
|
+
* predicate that faults because it names a scope ROOT the write never bound —
|
|
30
|
+
* `parent.status == 'paid'` with no master-detail header in hand — is
|
|
31
|
+
* fail-CLOSED on the server: `isReadonlyWhenLocked` warns and then returns
|
|
32
|
+
* TRUE, i.e. LOCKED (a declared lock is not waived merely because it could not
|
|
33
|
+
* be evaluated), and `stripReadonlyWhenFields` — plus
|
|
34
|
+
* `stripReadonlyWhenFieldsMulti` on the bulk path — DELETES that key from the
|
|
35
|
+
* UPDATE payload. This file keeps the same fault fail-OPEN. For that one class
|
|
36
|
+
* the two ends therefore point in OPPOSITE directions, deliberately: the
|
|
37
|
+
* framework's ADR-0124 D1 — server enforces, client is courtesy — makes the
|
|
38
|
+
* server the authority, so the courtesy layer does not get to guess "locked"
|
|
39
|
+
* and grey out a field the server might have accepted.
|
|
40
|
+
*
|
|
41
|
+
* The caller-visible consequence, spelled out because the symptom is silent:
|
|
42
|
+
* the form renders the field EDITABLE, the user edits it, the save reports
|
|
43
|
+
* SUCCESS, and the new value never lands — the server dropped the key and kept
|
|
44
|
+
* the persisted one. Debug that toward the SERVER-side lock (its `… treating
|
|
45
|
+
* the field as LOCKED` warning, and the write response's `droppedFields`), not
|
|
46
|
+
* toward the client predicate, which did exactly what it was asked to. The
|
|
47
|
+
* other two halves carry no such carve-out: `requiredWhen` binds the same
|
|
48
|
+
* `parent` scope but deliberately kept fail-open semantics (objectstack#4977),
|
|
49
|
+
* so an unevaluable requirement is skipped on BOTH ends, and field-level
|
|
50
|
+
* `visibleWhen` is not a server concept at all.
|
|
27
51
|
*
|
|
28
52
|
* Fail-open is **loud**, not silent (objectstack#5149): a predicate that
|
|
29
53
|
* cannot be evaluated — parse error, unbound identifier, engine fault — logs
|
|
@@ -31,8 +55,10 @@
|
|
|
31
55
|
* reason, then still returns the caller's fallback. Without the warning a
|
|
32
56
|
* broken predicate is indistinguishable from an absent one, so
|
|
33
57
|
* conditional-visibility bugs survive inspection indefinitely. This is the
|
|
34
|
-
* client half of the server's "log and allow" convention
|
|
35
|
-
* 'x' failed to evaluate — change
|
|
58
|
+
* client half of the server's "log and allow" convention for the faults where
|
|
59
|
+
* the two ends DO agree (`readonlyWhen for 'x' failed to evaluate — change
|
|
60
|
+
* allowed through` — the GENERIC-fault message; the unbound-root fault above
|
|
61
|
+
* logs `treating the field as LOCKED` instead). The *default* stays
|
|
36
62
|
* fail-open on purpose — flipping it is a shipped-behavior change tracked
|
|
37
63
|
* separately in objectstack#5149 (appeal 1, undecided).
|
|
38
64
|
*/
|
|
@@ -86,30 +112,38 @@ export function evalFieldPredicate(pred, record, fallback, previous, scope, diag
|
|
|
86
112
|
if (pred == null || (typeof pred === 'string' && !pred.trim()))
|
|
87
113
|
return fallback;
|
|
88
114
|
const expr = toExpression(pred);
|
|
115
|
+
// The two fault sources — a not-ok verdict and a throw that slipped past the
|
|
116
|
+
// engine's "never throws" contract — converge on one reason string and one
|
|
117
|
+
// reporting site below, so the built-in warning and the `onFault` passback
|
|
118
|
+
// can never describe the failure differently.
|
|
119
|
+
let reason;
|
|
120
|
+
let value = fallback;
|
|
89
121
|
try {
|
|
90
122
|
const res = ExpressionEngine.evaluate(expr, {
|
|
91
123
|
record,
|
|
92
124
|
previous,
|
|
93
125
|
...(scope ? { extra: scope } : {}),
|
|
94
126
|
});
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return fallback;
|
|
102
|
-
}
|
|
103
|
-
return res.value === true;
|
|
127
|
+
// Parse error, type error, unbound identifier, engine fault … — every
|
|
128
|
+
// not-ok verdict resolves to the fallback, but never silently (#5149).
|
|
129
|
+
if (!res.ok)
|
|
130
|
+
reason = `[${res.error.kind}] ${res.error.message}`;
|
|
131
|
+
else
|
|
132
|
+
value = res.value === true;
|
|
104
133
|
}
|
|
105
134
|
catch (err) {
|
|
106
|
-
|
|
107
|
-
|
|
135
|
+
reason = `[throw] ${err instanceof Error ? err.message : String(err)}`;
|
|
136
|
+
}
|
|
137
|
+
if (reason !== undefined) {
|
|
108
138
|
if (diagnostic?.warn !== false) {
|
|
109
|
-
warnPredicateFailure(expr, fallback,
|
|
139
|
+
warnPredicateFailure(expr, fallback, reason, diagnostic?.context);
|
|
110
140
|
}
|
|
141
|
+
// Outside the try on purpose: a throwing callback is a caller bug and must
|
|
142
|
+
// surface as one, not be swallowed and re-reported as an engine fault.
|
|
143
|
+
diagnostic?.onFault?.(reason);
|
|
111
144
|
return fallback;
|
|
112
145
|
}
|
|
146
|
+
return value;
|
|
113
147
|
}
|
|
114
148
|
/**
|
|
115
149
|
* Resolve the effective `{ visible, readonly, required }` state for a field
|
|
@@ -118,6 +152,24 @@ export function evalFieldPredicate(pred, record, fallback, previous, scope, diag
|
|
|
118
152
|
* `false` predicate result for required/readonly — but `visibleWhen` is
|
|
119
153
|
* authoritative when present (so a field can be conditionally shown/hidden).
|
|
120
154
|
*
|
|
155
|
+
* ## `statics.serverOwnedValue` — the one thing that outranks BOTH
|
|
156
|
+
*
|
|
157
|
+
* A CREATE form leaves a producer-owned control empty on purpose and omits the
|
|
158
|
+
* key, so the server resolves the declared runtime default (#4069). Required-
|
|
159
|
+
* ness is then not the client's question at all, and this is the single place
|
|
160
|
+
* that answers it: `required` resolves to `false` whatever `statics.required`
|
|
161
|
+
* says and whatever `requiredWhen` evaluates to.
|
|
162
|
+
*
|
|
163
|
+
* Both spellings had to land HERE, not in a gate a layer up. Since
|
|
164
|
+
* objectui#4201 the submit-time check and the required marker read one live
|
|
165
|
+
* verdict — this function's — so a suppression applied anywhere else would
|
|
166
|
+
* show a field without an asterisk and still refuse the submit, which is the
|
|
167
|
+
* exact two-layer disagreement #4201 removed. #4085 is the conditional half:
|
|
168
|
+
* `requiredWhen` resolving TRUE on a create form re-required a control the
|
|
169
|
+
* renderer deliberately left empty, with nothing the user could type to
|
|
170
|
+
* unblock it. See `isServerOwnedValue` in `../validation/server-owned-value.js`
|
|
171
|
+
* for who computes the fact and why the mode is the caller's to state.
|
|
172
|
+
*
|
|
121
173
|
* @param fieldContext Optional locator for failure warnings — e.g.
|
|
122
174
|
* `"field 'amount'"`. The warning then reads
|
|
123
175
|
* `visibleWhen of field 'amount' …`; without it, the rule
|
|
@@ -134,9 +186,16 @@ export function resolveFieldRuleState(rules, record, statics, previous, scope, f
|
|
|
134
186
|
(rules.readonlyWhen != null
|
|
135
187
|
? evalFieldPredicate(rules.readonlyWhen, record, false, previous, scope, diag('readonlyWhen'))
|
|
136
188
|
: false);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
189
|
+
// Short-circuited, not evaluated-and-discarded: the verdict cannot depend on
|
|
190
|
+
// the predicate, so running it would only spend an engine call per field per
|
|
191
|
+
// render. A broken `requiredWhen` still warns wherever it decides something
|
|
192
|
+
// — the same object's EDIT form, and any create form where the field is not
|
|
193
|
+
// server-owned.
|
|
194
|
+
const required = statics.serverOwnedValue === true
|
|
195
|
+
? false
|
|
196
|
+
: statics.required === true ||
|
|
197
|
+
(rules.requiredWhen != null
|
|
198
|
+
? evalFieldPredicate(rules.requiredWhen, record, false, previous, scope, diag('requiredWhen'))
|
|
199
|
+
: false);
|
|
141
200
|
return { visible, readonly, required };
|
|
142
201
|
}
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
export * from './ExpressionContext.js';
|
|
9
9
|
export * from './ExpressionEvaluator.js';
|
|
10
10
|
export * from './predicateInput.js';
|
|
11
|
+
export * from './declaredPredicate.js';
|
|
11
12
|
export * from './fieldRules.js';
|
|
13
|
+
export * from './rowPredicateCanon.js';
|
|
12
14
|
export * from './listConditional.js';
|
|
13
15
|
export * from './optionRules.js';
|
|
14
16
|
export * from './optionLint.js';
|
package/dist/evaluator/index.js
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
export * from './ExpressionContext.js';
|
|
9
9
|
export * from './ExpressionEvaluator.js';
|
|
10
10
|
export * from './predicateInput.js';
|
|
11
|
+
export * from './declaredPredicate.js';
|
|
11
12
|
export * from './fieldRules.js';
|
|
13
|
+
export * from './rowPredicateCanon.js';
|
|
12
14
|
export * from './listConditional.js';
|
|
13
15
|
export * from './optionRules.js';
|
|
14
16
|
export * from './optionLint.js';
|