@object-ui/core 16.1.0 → 17.1.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +1355 -4
  2. package/dist/actions/ActionRunner.d.ts +82 -14
  3. package/dist/actions/ActionRunner.js +164 -81
  4. package/dist/actions/actionKeys.d.ts +118 -0
  5. package/dist/actions/actionKeys.js +243 -0
  6. package/dist/actions/index.d.ts +1 -0
  7. package/dist/actions/index.js +1 -0
  8. package/dist/adapters/ApiDataSource.js +10 -1
  9. package/dist/adapters/ValueDataSource.d.ts +2 -2
  10. package/dist/evaluator/fieldRules.d.ts +0 -2
  11. package/dist/evaluator/fieldRules.js +3 -2
  12. package/dist/evaluator/optionLint.js +5 -2
  13. package/dist/evaluator/optionRules.d.ts +13 -6
  14. package/dist/evaluator/optionRules.js +0 -23
  15. package/dist/index.d.ts +4 -0
  16. package/dist/index.js +7 -0
  17. package/dist/registry/Registry.d.ts +81 -2
  18. package/dist/registry/Registry.js +102 -2
  19. package/dist/registry/public-blocks.js +52 -1
  20. package/dist/theme/ThemeEngine.js +0 -12
  21. package/dist/utils/chart-series.d.ts +28 -0
  22. package/dist/utils/chart-series.js +55 -0
  23. package/dist/utils/column-identity.d.ts +110 -0
  24. package/dist/utils/column-identity.js +210 -0
  25. package/dist/utils/dashboard-filters.d.ts +2 -2
  26. package/dist/utils/expand-fields.js +8 -8
  27. package/dist/utils/filter-converter.d.ts +47 -0
  28. package/dist/utils/filter-converter.js +94 -7
  29. package/dist/utils/filter-tokens.d.ts +101 -0
  30. package/dist/utils/filter-tokens.js +173 -0
  31. package/dist/utils/managedBy.d.ts +21 -3
  32. package/dist/utils/managedBy.js +45 -4
  33. package/dist/utils/normalize-list-view.d.ts +99 -0
  34. package/dist/utils/normalize-list-view.js +255 -0
  35. package/dist/utils/sort-values.d.ts +52 -0
  36. package/dist/utils/sort-values.js +125 -0
  37. package/dist/validation/index.d.ts +9 -1
  38. package/dist/validation/index.js +9 -1
  39. package/dist/validation/validators/index.d.ts +7 -1
  40. package/dist/validation/validators/index.js +7 -1
  41. package/dist/validation/validators/object-validation-engine.d.ts +151 -31
  42. package/dist/validation/validators/object-validation-engine.js +275 -142
  43. package/package.json +4 -4
@@ -0,0 +1,52 @@
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
+ /** A cell value reduced to something two rows can be ordered by. */
9
+ export type SortValue = string | number | boolean | null;
10
+ export interface SortValueOptions {
11
+ /**
12
+ * `id → label` map for relational columns whose cell value is still a RAW id
13
+ * because the surface resolves labels client-side instead of asking the
14
+ * server for `$expand` (RelatedList does exactly this). When the raw value
15
+ * has an entry, the label the cell renders becomes the sort key.
16
+ */
17
+ labels?: Record<string, string>;
18
+ }
19
+ /**
20
+ * Reduce one cell value to the primitive a client-side sort should order by.
21
+ *
22
+ * - empty (`null` / `undefined` / `''`) → `null`, so callers can place blanks
23
+ * consistently rather than sorting `undefined` against a string;
24
+ * - a `labels` hit (by the raw id) wins — that IS the string in the cell;
25
+ * - an expanded reference record resolves through {@link getRecordDisplayName},
26
+ * the ONE shared display-name resolver (ADR-0079), so the sort key and the
27
+ * lookup cell agree on which field names a record;
28
+ * - arrays join their resolved parts, mirroring how a multi-value cell reads;
29
+ * - primitives pass through unchanged (numbers stay numbers, so a number column
30
+ * still sorts numerically rather than lexicographically).
31
+ */
32
+ export declare function getSortValue(value: unknown, options?: SortValueOptions): SortValue;
33
+ /**
34
+ * Compare two {@link SortValue}s **ascending**.
35
+ *
36
+ * Callers negate the result for descending, which is also why blanks are placed
37
+ * last here rather than "always last": negating moves them to the front, the
38
+ * same convention the inline comparators this replaces already had.
39
+ *
40
+ * Same-typed values compare in their own domain (numbers numerically, booleans
41
+ * false-before-true); anything else falls back to a locale-aware string compare
42
+ * with `numeric: true`, so `Item 2` precedes `Item 10`.
43
+ */
44
+ export declare function compareSortValues(a: SortValue, b: SortValue): number;
45
+ /**
46
+ * Convenience wrapper: reduce both cell values and compare them ascending.
47
+ *
48
+ * Prefer resolving the sort key ONCE per row (decorate → sort → undecorate)
49
+ * when sorting a whole collection — `getRecordDisplayName` walks the record, so
50
+ * calling it inside the comparator repeats that walk `O(n log n)` times.
51
+ */
52
+ export declare function compareCellValues(a: unknown, b: unknown, options?: SortValueOptions): number;
@@ -0,0 +1,125 @@
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
+ * sort-values — reduce a *cell value* to the primitive a CLIENT-SIDE sort should
10
+ * order by, and compare two of those primitives (objectui#3096).
11
+ *
12
+ * Why this exists: a relational column (`lookup` / `master_detail` / `tree` /
13
+ * `user` — see `EXPANDABLE_FIELD_TYPES`) does not hold the string its cell
14
+ * shows. Depending on the surface it holds either the `$expand`-ed related
15
+ * RECORD (`{ id, name, … }`) or a raw foreign-key id whose label was resolved
16
+ * separately. Both break the naive comparators these surfaces used to inline:
17
+ *
18
+ * - `a[col] < b[col]` on two objects is ALWAYS false, so the comparator
19
+ * collapses to the constant `1` and the "sort" permutes rows into an order
20
+ * with no relation to anything on screen;
21
+ * - `String(a[col])` on an object is `"[object Object]"` — every row compares
22
+ * equal, so the sort silently does nothing;
23
+ * - comparing raw ids (`rec_7f3…`) orders a column of human-readable names by
24
+ * an opaque key, which reads to the user as "sorting is broken".
25
+ *
26
+ * Reducing the value to the label the cell renders makes a client-side sort
27
+ * order by what the user actually sees. This CANNOT fix a server `$orderby`,
28
+ * where the sort key is the stored id by construction — surfaces that sort on
29
+ * the server must instead not offer relational columns as sort keys (that half
30
+ * of #3096 lives at each entry point).
31
+ *
32
+ * Pure (no React / i18n), so every renderer package can share it.
33
+ */
34
+ import { getRecordDisplayName } from './record-title.js';
35
+ /**
36
+ * Reduce one cell value to the primitive a client-side sort should order by.
37
+ *
38
+ * - empty (`null` / `undefined` / `''`) → `null`, so callers can place blanks
39
+ * consistently rather than sorting `undefined` against a string;
40
+ * - a `labels` hit (by the raw id) wins — that IS the string in the cell;
41
+ * - an expanded reference record resolves through {@link getRecordDisplayName},
42
+ * the ONE shared display-name resolver (ADR-0079), so the sort key and the
43
+ * lookup cell agree on which field names a record;
44
+ * - arrays join their resolved parts, mirroring how a multi-value cell reads;
45
+ * - primitives pass through unchanged (numbers stay numbers, so a number column
46
+ * still sorts numerically rather than lexicographically).
47
+ */
48
+ export function getSortValue(value, options) {
49
+ if (value === null || value === undefined || value === '')
50
+ return null;
51
+ const labels = options?.labels;
52
+ if (typeof value === 'string') {
53
+ return labels?.[value] ?? value;
54
+ }
55
+ if (typeof value === 'number') {
56
+ if (!Number.isFinite(value))
57
+ return null;
58
+ // A numeric foreign key still deserves its resolved label.
59
+ return labels?.[String(value)] ?? value;
60
+ }
61
+ if (typeof value === 'boolean')
62
+ return value;
63
+ if (value instanceof Date) {
64
+ const time = value.getTime();
65
+ return Number.isNaN(time) ? null : time;
66
+ }
67
+ if (Array.isArray(value)) {
68
+ const parts = value
69
+ .map((entry) => getSortValue(entry, options))
70
+ .filter((entry) => entry !== null)
71
+ .map((entry) => String(entry));
72
+ return parts.length > 0 ? parts.join(', ') : null;
73
+ }
74
+ if (typeof value === 'object') {
75
+ const record = value;
76
+ // An expanded record can still carry a client-resolved label (the surface
77
+ // built its map from ids, the server expanded the same field).
78
+ const id = record.id ?? record._id;
79
+ if (id !== null && id !== undefined) {
80
+ const mapped = labels?.[String(id)];
81
+ if (mapped)
82
+ return mapped;
83
+ }
84
+ // `fallback: ''` so a record with nothing name-like reduces to `null`
85
+ // (sorted with the blanks) instead of the literal string "Untitled",
86
+ // which would bunch unrelated empty records under "U".
87
+ const name = getRecordDisplayName(undefined, record, { fallback: '' });
88
+ return name === '' ? null : name;
89
+ }
90
+ return String(value);
91
+ }
92
+ /**
93
+ * Compare two {@link SortValue}s **ascending**.
94
+ *
95
+ * Callers negate the result for descending, which is also why blanks are placed
96
+ * last here rather than "always last": negating moves them to the front, the
97
+ * same convention the inline comparators this replaces already had.
98
+ *
99
+ * Same-typed values compare in their own domain (numbers numerically, booleans
100
+ * false-before-true); anything else falls back to a locale-aware string compare
101
+ * with `numeric: true`, so `Item 2` precedes `Item 10`.
102
+ */
103
+ export function compareSortValues(a, b) {
104
+ if (a === null && b === null)
105
+ return 0;
106
+ if (a === null)
107
+ return 1;
108
+ if (b === null)
109
+ return -1;
110
+ if (typeof a === 'boolean' && typeof b === 'boolean')
111
+ return (a ? 1 : 0) - (b ? 1 : 0);
112
+ if (typeof a === 'number' && typeof b === 'number')
113
+ return a - b;
114
+ return String(a).localeCompare(String(b), undefined, { numeric: true });
115
+ }
116
+ /**
117
+ * Convenience wrapper: reduce both cell values and compare them ascending.
118
+ *
119
+ * Prefer resolving the sort key ONCE per row (decorate → sort → undecorate)
120
+ * when sorting a whole collection — `getRecordDisplayName` walks the record, so
121
+ * calling it inside the comparator repeats that walk `O(n log n)` times.
122
+ */
123
+ export function compareCellValues(a, b, options) {
124
+ return compareSortValues(getSortValue(a, options), getSortValue(b, options));
125
+ }
@@ -2,7 +2,15 @@
2
2
  * @object-ui/core - Validation Module
3
3
  *
4
4
  * Phase 3.5: Validation engine
5
- * ObjectStack Spec v2.0.1: Object-level validation
5
+ *
6
+ * Object-level validation. The rule vocabulary is owned by
7
+ * `@objectstack/spec/data` and derived in `@object-ui/types`; canonicity is
8
+ * carried by that derivation and its parity gate, not by this comment.
9
+ *
10
+ * The object-level rule ENGINE (`validators/`) is @deprecated (#3110) — the
11
+ * server is the single implementation of rule enforcement. Field/record SHAPE
12
+ * validation (`schema-validator.js`, `validation-engine.js`) is unaffected: it
13
+ * is client-side form validation, not a mirror of a server rule.
6
14
  */
7
15
  export * from './validation-engine.js';
8
16
  export * from './schema-validator.js';
@@ -2,7 +2,15 @@
2
2
  * @object-ui/core - Validation Module
3
3
  *
4
4
  * Phase 3.5: Validation engine
5
- * ObjectStack Spec v2.0.1: Object-level validation
5
+ *
6
+ * Object-level validation. The rule vocabulary is owned by
7
+ * `@objectstack/spec/data` and derived in `@object-ui/types`; canonicity is
8
+ * carried by that derivation and its parity gate, not by this comment.
9
+ *
10
+ * The object-level rule ENGINE (`validators/`) is @deprecated (#3110) — the
11
+ * server is the single implementation of rule enforcement. Field/record SHAPE
12
+ * validation (`schema-validator.js`, `validation-engine.js`) is unaffected: it
13
+ * is client-side form validation, not a mirror of a server rule.
6
14
  */
7
15
  export * from './validation-engine.js';
8
16
  export * from './schema-validator.js';
@@ -8,7 +8,13 @@
8
8
  /**
9
9
  * @object-ui/core - Validators
10
10
  *
11
- * ObjectStack Spec v2.0.1 compliant validators
11
+ * Publishes the @deprecated `ObjectValidationEngine` (#3110). Object-level
12
+ * validation rules are enforcement, and enforcement is single-implementation on
13
+ * the server (`objectql`'s rule-validator); objectui renders the server's
14
+ * rejection rather than predicting it, and pre-submit feedback belongs in a
15
+ * validate-only (dry-run) write. This barrel keeps the API exported for host
16
+ * applications that already depend on it — re-exporting is not wiring, and
17
+ * `validation-engine-stays-unwired.test.ts` enforces that distinction.
12
18
  *
13
19
  * @module validators
14
20
  * @packageDocumentation
@@ -8,7 +8,13 @@
8
8
  /**
9
9
  * @object-ui/core - Validators
10
10
  *
11
- * ObjectStack Spec v2.0.1 compliant validators
11
+ * Publishes the @deprecated `ObjectValidationEngine` (#3110). Object-level
12
+ * validation rules are enforcement, and enforcement is single-implementation on
13
+ * the server (`objectql`'s rule-validator); objectui renders the server's
14
+ * rejection rather than predicting it, and pre-submit feedback belongs in a
15
+ * validate-only (dry-run) write. This barrel keeps the API exported for host
16
+ * applications that already depend on it — re-exporting is not wiring, and
17
+ * `validation-engine-stays-unwired.test.ts` enforces that distinction.
12
18
  *
13
19
  * @module validators
14
20
  * @packageDocumentation
@@ -6,23 +6,92 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  /**
9
- * @object-ui/core - Object-Level Validation Engine
10
- *
11
- * ObjectStack Spec v2.0.1 compliant validation engine for object-level validation rules.
12
- * Supports all 9 validation types from the specification:
13
- * - ScriptValidation
14
- * - UniquenessValidation
15
- * - StateMachineValidation
16
- * - CrossFieldValidation
17
- * - AsyncValidation
18
- * - ConditionalValidation
19
- * - FormatValidation
20
- * - RangeValidation
9
+ * @object-ui/core - Object-Level Validation Engine — **DEPRECATED** (#3110)
10
+ *
11
+ * A client-side pre-check of the rules an object declares in
12
+ * `ObjectSchema.validations`. The authority is the server
13
+ * (`objectql/src/validation/rule-validator.ts`), which evaluates the same rules
14
+ * on the write path.
15
+ *
16
+ * ## Why this is deprecated rather than finished
17
+ *
18
+ * Validation rules are ENFORCEMENT, and enforcement is single-implementation on
19
+ * the server by design. `evaluator/fieldRules.ts` draws that line explicitly:
20
+ * PRESENTATION predicates (`visibleWhen` / `readonlyWhen` / `requiredWhen`) are
21
+ * evaluated client-side by delegating to the canonical `ExpressionEngine`,
22
+ * "rather than re-implementing a parallel evaluator" (ADR-0036). This module is
23
+ * that parallel evaluator, on the enforcement side of the line.
24
+ *
25
+ * #3103 converged its semantics onto the server rule-for-rule, with eight
26
+ * mutation-tested gates — and it STILL left a known divergence: the server
27
+ * carries ADR-0113's legacy-violation exemption (reject only when the merged
28
+ * state violates AND this write makes it worse, so a pre-existing violation on
29
+ * an old row passes), and this engine does not. Editing an unrelated field on a
30
+ * legacy row would be blocked here and accepted there. That is the argument in
31
+ * one line: mirroring cross-repo behaviour is structurally unreliable, not
32
+ * unreliable-this-time.
33
+ *
34
+ * ## What to use instead
35
+ *
36
+ * Let the write fail and render the server's rejection — it is already
37
+ * structured (`field` / `code` / `message`, plus a label since objectstack#3957).
38
+ * If pre-submit feedback is wanted, the answer is a **validate-only (dry-run)
39
+ * write** on the server: identical UX, zero parity risk, and it also covers the
40
+ * two rule kinds a client can never check — `unique` (needs the database) and
41
+ * `json_schema` (ajv lives server-side).
42
+ *
43
+ * Nothing here has been removed or changed in behaviour; existing callers keep
44
+ * working. `validation-engine-stays-unwired.test.ts` fails if a production
45
+ * module starts importing this one, so the decision is a mechanism rather than
46
+ * this comment (#3017).
47
+ *
48
+ * ## When to revisit
49
+ *
50
+ * Offline writes (`OfflineConfig`). With no server to ask, a client evaluator
51
+ * stops being redundant and becomes necessary. The precondition then is
52
+ * conformance fixtures shipped by `@objectstack/spec` — golden
53
+ * (rule, record, verdict) triples both repos run in CI — because that is the
54
+ * only thing that turns cross-repo parity into a mechanism instead of a habit.
55
+ * Reverse the decision in #3110 before reversing it here.
56
+ *
57
+ * ## Semantics (unchanged, for as long as this exists)
58
+ *
59
+ * The rule types come from `@object-ui/types`, which derives them from
60
+ * `@objectstack/spec/data` (objectstack#4115). Semantics mirrored from the
61
+ * server:
62
+ *
63
+ * - **Polarity.** A `script` / `cross_field` `condition` and a `conditional`
64
+ * `when` express the FAILURE condition: predicate TRUE ⇒ rule VIOLATED.
65
+ * - **Fail-open on broken, fail-closed on violated.** A predicate that cannot
66
+ * be evaluated (parse error, unknown identifier, unusable expression
67
+ * envelope) is a broken rule, not a violated one — it is skipped, not
68
+ * reported. Likewise a malformed `regex`.
69
+ * - **Spec defaults, not falsy reads.** `active` defaults to `true` and
70
+ * `events` to `['insert','update']` — they arrive absent from `/meta`
71
+ * because the spec applies those defaults at parse time. Reading an absent
72
+ * `active` as "off" would silently disable every rule authored without it.
73
+ * - **`priority` orders execution** (lower first, stable).
74
+ * - **`format` and `state_machine` only fire when the write TOUCHES the
75
+ * field**, matching the server; emptiness and requiredness belong to the
76
+ * field-shape validator.
77
+ *
78
+ * Two documented divergences from the server, both in the safe direction:
79
+ *
80
+ * 1. The default evaluator is a small non-CEL parser (see
81
+ * `SimpleExpressionEvaluator`), so a predicate using real CEL constructs
82
+ * evaluates to nothing and fails open. Pass a CEL-backed
83
+ * `ValidationExpressionEvaluator` to close that gap.
84
+ * 2. `unique` / `async` / `range` rules are still dispatched. They are
85
+ * @deprecated objectui-local variants that the spec's `ValidationRuleSchema`
86
+ * rejects outright, so they cannot arrive from `/meta` — only a caller
87
+ * building rules by hand can produce one.
21
88
  *
22
89
  * @module object-validation-engine
23
90
  * @packageDocumentation
24
91
  */
25
92
  import type { ObjectValidationRule } from '@object-ui/types';
93
+ /** Write contexts a rule can run on. The spec retired `delete` (objectstack#3184). */
94
+ export type ObjectValidationEvent = 'insert' | 'update';
26
95
  /**
27
96
  * Validation context for object-level validations
28
97
  */
@@ -56,63 +125,114 @@ export interface ValidationExpressionEvaluator {
56
125
  evaluate(expression: string, context: Record<string, any>): any;
57
126
  }
58
127
  /**
59
- * Object-Level Validation Engine
60
- * Implements ObjectStack Spec v2.0.1 validation framework
128
+ * Object-Level Validation Engine — the client pre-check described at the top of
129
+ * this module.
130
+ *
131
+ * @deprecated (#3110) Validation rules are enforcement, and enforcement is
132
+ * single-implementation on the server (`objectql`'s rule-validator). Render the
133
+ * server's rejection instead; for pre-submit feedback, use a validate-only
134
+ * (dry-run) write. Still functional and still correct as of #3103 — but it
135
+ * cannot stay in step with the server without a conformance-fixture mechanism
136
+ * that does not exist yet. See the module header.
61
137
  */
62
138
  export declare class ObjectValidationEngine {
63
139
  private expressionEvaluator;
64
140
  private uniquenessChecker?;
65
141
  constructor(expressionEvaluator?: ValidationExpressionEvaluator, uniquenessChecker?: (fields: string[], values: Record<string, any>, scope?: string, context?: ObjectValidationContext) => Promise<boolean>);
66
142
  /**
67
- * Validate a record against a set of validation rules
143
+ * Validate a record against a set of validation rules.
144
+ *
145
+ * Rules run in `priority` order (lower first, stable within a priority), and
146
+ * only when `active` is not explicitly `false` and `events` covers this write
147
+ * context — reading the spec's defaults, not the falsiness of an absent key.
68
148
  */
69
- validateRecord(rules: ObjectValidationRule[], context: ObjectValidationContext, event?: 'insert' | 'update' | 'delete'): Promise<ObjectValidationResult[]>;
149
+ validateRecord(rules: ObjectValidationRule[], context: ObjectValidationContext, event?: ObjectValidationEvent): Promise<ObjectValidationResult[]>;
70
150
  /**
71
- * Validate a single rule
151
+ * Validate a single rule.
152
+ *
153
+ * `rule` is widened past `ObjectValidationRule` because a `conditional`'s
154
+ * `then` / `otherwise` branch may be any rule the SPEC's union admits —
155
+ * including `json_schema`, which this engine does not implement.
72
156
  */
73
157
  private validateRule;
74
158
  /**
75
- * Validate script-based rule
159
+ * The evaluation scope for a predicate.
160
+ *
161
+ * The server binds `{ record, previous }`, so a spec-authored predicate reads
162
+ * `record.amount`. objectui's own rules have always been written against the
163
+ * bare field name (`amount`). Both are bound — the roots last so a field
164
+ * literally called `record` cannot shadow them.
76
165
  */
77
- private validateScript;
166
+ private scopeFor;
167
+ /**
168
+ * Evaluate a `script` / `cross_field` predicate.
169
+ *
170
+ * Both variants share one path on the server, and so do they here. The
171
+ * predicate is the FAILURE condition: TRUE means the rule is violated. A
172
+ * predicate that cannot be evaluated is a BROKEN rule and fails open.
173
+ */
174
+ private validatePredicate;
78
175
  /**
79
176
  * Validate uniqueness constraint
80
177
  */
81
178
  private validateUniqueness;
82
179
  /**
83
- * Validate state machine transitions
180
+ * Validate state machine transitions (ADR-0020).
181
+ *
182
+ * A flat `field` plus a `{ from: [allowedTo] }` map. Semantics mirror the
183
+ * server-side rule-validator:
184
+ *
185
+ * INSERT
186
+ * - no `initialStates` declared → allow (legacy no-op)
187
+ * - field absent from the write → allow (requiredness is not our job)
188
+ * - value empty → allow (ditto)
189
+ * - value ∉ initialStates → reject (the FSM entry point, #3165)
190
+ *
191
+ * UPDATE
192
+ * - field absent from the write → allow (no transition to police)
193
+ * - no prior record → allow (cannot reason)
194
+ * - field unchanged, or cleared → allow (no transition)
195
+ * - `from` not declared in the map → lenient allow (unconstrained)
196
+ * - `to` ∉ transitions[from] → reject
84
197
  */
85
198
  private validateStateMachine;
86
- /**
87
- * Validate cross-field constraints
88
- */
89
- private validateCrossField;
90
199
  /**
91
200
  * Validate async/remote validation
92
201
  */
93
202
  private validateAsync;
94
203
  /**
95
- * Validate conditional rules
204
+ * Validate a conditional rule.
205
+ *
206
+ * Evaluates `when`, then recurses into `then` (TRUE) or `otherwise` (FALSE).
207
+ * The nested rule supplies the violation; an un-evaluable `when` is a broken
208
+ * rule and fails open. A branch whose own `active` is explicitly `false` is
209
+ * skipped, matching the server.
96
210
  */
97
211
  private validateConditional;
98
212
  /**
99
- * Validate format/pattern
213
+ * Validate a field's format against `regex` and/or a named `format`.
214
+ *
215
+ * Mirrors the server: the rule only fires when the write TOUCHES the field and
216
+ * the value is non-empty (requiredness and type-shape belong to the
217
+ * field-level validator, so an absent or blank value is not a format
218
+ * violation). Both checks apply when both are declared. A `regex` that will
219
+ * not compile is a BROKEN rule — logged and skipped, never a violation.
100
220
  */
101
221
  private validateFormat;
102
222
  /**
103
223
  * Validate range constraints
104
224
  */
105
225
  private validateRange;
106
- /**
107
- * Get predefined regex pattern
108
- */
109
- private getPredefinedPattern;
110
226
  }
111
227
  /**
112
228
  * Default instance
229
+ *
230
+ * @deprecated (#3110) See `ObjectValidationEngine`.
113
231
  */
114
232
  export declare const defaultObjectValidationEngine: ObjectValidationEngine;
115
233
  /**
116
234
  * Convenience function to validate a record
235
+ *
236
+ * @deprecated (#3110) See `ObjectValidationEngine`.
117
237
  */
118
- export declare function validateRecord(rules: ObjectValidationRule[], context: ObjectValidationContext, event?: 'insert' | 'update' | 'delete'): Promise<ObjectValidationResult[]>;
238
+ export declare function validateRecord(rules: ObjectValidationRule[], context: ObjectValidationContext, event?: ObjectValidationEvent): Promise<ObjectValidationResult[]>;