@object-ui/react 17.6.0 → 18.0.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 +4263 -0
- package/README.md +383 -42
- package/dist/SchemaRenderer.d.ts +64 -7
- package/dist/SchemaRenderer.js +1326 -106
- package/dist/context/ActionContext.js +40 -1
- package/dist/context/AppShellContext.d.ts +27 -0
- package/dist/context/DiscussionContext.d.ts +27 -0
- package/dist/context/DiscussionContext.js +4 -0
- package/dist/context/DrillNavigationContext.d.ts +13 -0
- package/dist/context/InlineEditContext.d.ts +151 -1
- package/dist/context/InlineEditContext.js +36 -2
- package/dist/context/PrintPageContext.d.ts +60 -0
- package/dist/context/PrintPageContext.js +32 -0
- package/dist/context/RecordContext.d.ts +48 -5
- package/dist/context/RecordContext.js +10 -1
- package/dist/context/RelatedRecordActionsContext.d.ts +72 -2
- package/dist/context/RelatedRecordActionsContext.js +4 -0
- package/dist/context/SchemaRendererContext.d.ts +61 -6
- package/dist/context/SchemaRendererContext.js +39 -6
- package/dist/context/ThemeContext.js +1 -1
- package/dist/context/actionRunnerTranslator.d.ts +27 -0
- package/dist/context/actionRunnerTranslator.js +37 -0
- package/dist/context/index.d.ts +1 -0
- package/dist/context/index.js +1 -0
- package/dist/element-data-source/ElementApiDataSourcePanel.d.ts +28 -0
- package/dist/element-data-source/ElementApiDataSourcePanel.js +10 -0
- package/dist/element-data-source/ElementDataSourceGate.d.ts +118 -3
- package/dist/element-data-source/ElementDataSourceGate.js +401 -19
- package/dist/element-data-source/useElementApiDataSource.d.ts +119 -0
- package/dist/element-data-source/useElementApiDataSource.js +198 -0
- package/dist/hooks/index.d.ts +3 -1
- package/dist/hooks/index.js +10 -1
- package/dist/hooks/useActionEngine.js +46 -3
- package/dist/hooks/useActionRunner.js +4 -0
- package/dist/hooks/useActionTextLocalizer.d.ts +35 -9
- package/dist/hooks/useActionTextLocalizer.js +70 -14
- package/dist/hooks/useConfigBagEvaluator.d.ts +31 -0
- package/dist/hooks/useConfigBagEvaluator.js +40 -0
- package/dist/hooks/useDatasetDimensionLabels.js +25 -10
- package/dist/hooks/useDensityMode.d.ts +8 -2
- package/dist/hooks/useDensityMode.js +8 -2
- package/dist/hooks/useDiscovery.d.ts +1 -10
- package/dist/hooks/useETagCache.js +25 -4
- package/dist/hooks/useElementDataSource.d.ts +15 -2
- package/dist/hooks/useElementDataSource.js +24 -7
- package/dist/hooks/useExpression.d.ts +52 -59
- package/dist/hooks/useExpression.js +58 -90
- package/dist/hooks/useFilterScope.d.ts +22 -2
- package/dist/hooks/useFilterScope.js +29 -4
- package/dist/hooks/useGlobalUndo.d.ts +10 -0
- package/dist/hooks/useGlobalUndo.js +112 -46
- package/dist/hooks/useNavigationOverlay.d.ts +76 -11
- package/dist/hooks/useNavigationOverlay.js +119 -9
- package/dist/hooks/useOffline.js +65 -11
- package/dist/hooks/usePageAssignment.d.ts +33 -7
- package/dist/hooks/usePageAssignment.js +85 -21
- package/dist/hooks/useRecordSearch.d.ts +34 -1
- package/dist/hooks/useRecordSearch.js +42 -7
- package/dist/hooks/useResolvedFilter.d.ts +46 -0
- package/dist/hooks/useResolvedFilter.js +130 -0
- package/dist/hooks/useSchemaPersistence.d.ts +7 -0
- package/dist/hooks/useSchemaPersistence.js +144 -5
- package/dist/hooks/useSettledSchema.d.ts +123 -0
- package/dist/hooks/useSettledSchema.js +186 -0
- package/dist/hooks/useViewData.d.ts +10 -3
- package/dist/hooks/useViewData.js +5 -14
- package/dist/index.d.ts +9 -1
- package/dist/index.js +48 -1
- package/dist/schema-input.d.ts +38 -8
- package/dist/schema-input.js +39 -7
- package/dist/utils/configBag.d.ts +68 -0
- package/dist/utils/configBag.js +70 -0
- package/dist/utils/error-message.d.ts +1 -1
- package/dist/utils/error-message.js +1 -1
- package/dist/utils/inlineAria.d.ts +61 -0
- package/dist/utils/inlineAria.js +70 -0
- package/dist/utils/localeMap.d.ts +27 -0
- package/dist/utils/localeMap.js +106 -0
- package/dist/utils/nonGridRowCeiling.d.ts +44 -0
- package/dist/utils/nonGridRowCeiling.js +78 -0
- package/dist/utils/paramsBag.d.ts +28 -0
- package/dist/utils/paramsBag.js +146 -0
- package/dist/utils/propsBagDiagnostic.d.ts +196 -0
- package/dist/utils/propsBagDiagnostic.js +422 -0
- package/dist/utils/refusedDataPropDiagnostic.d.ts +44 -0
- package/dist/utils/refusedDataPropDiagnostic.js +94 -0
- package/dist/utils/unevaluatedExpression.d.ts +44 -1
- package/dist/utils/unevaluatedExpression.js +92 -11
- package/dist/utils/visibilityDiagnostic.d.ts +282 -21
- package/dist/utils/visibilityDiagnostic.js +321 -46
- package/package.json +10 -10
- package/dist/hooks/useClientNotifications.d.ts +0 -47
- package/dist/hooks/useClientNotifications.js +0 -162
- package/dist/spec-bridge/SpecBridge.d.ts +0 -28
- package/dist/spec-bridge/SpecBridge.js +0 -56
- package/dist/spec-bridge/bridges/form-view.d.ts +0 -158
- package/dist/spec-bridge/bridges/form-view.js +0 -170
- package/dist/spec-bridge/bridges/list-view.d.ts +0 -20
- package/dist/spec-bridge/bridges/list-view.js +0 -268
- package/dist/spec-bridge/index.d.ts +0 -11
- package/dist/spec-bridge/index.js +0 -10
- package/dist/spec-bridge/types.d.ts +0 -55
- package/dist/spec-bridge/types.js +0 -8
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
|
+
import { isConfigBag } from './configBag.js';
|
|
9
|
+
import { isInlineLocaleMap, mapLocaleMapMembers } from './localeMap.js';
|
|
10
|
+
import { PARAMS_KEY, isParamsBag, mapParamsLeaves } from './paramsBag.js';
|
|
8
11
|
/**
|
|
9
12
|
* Dev-build diagnostic: an UNEVALUATED template expression reached the DOM.
|
|
10
13
|
*
|
|
@@ -55,6 +58,21 @@
|
|
|
55
58
|
* diagnostic would be louder than the contract it speaks for. Deepening
|
|
56
59
|
* evaluation is a separate decision, and this scan follows it the day it is
|
|
57
60
|
* taken, not before.
|
|
61
|
+
*
|
|
62
|
+
* That decision has been taken for exactly ONE key: `params` (objectui#7867,
|
|
63
|
+
* ruling A). Every string leaf of a `params` bag is evaluated, at any depth,
|
|
64
|
+
* on every channel — so this scan walks the same leaves, through the same
|
|
65
|
+
* walker (`paramsBag.ts`, which the evaluation memo also uses), and a template
|
|
66
|
+
* that still cannot resolve is reported by its path (`params.target.id`,
|
|
67
|
+
* `properties.params.ids[0]`). Radius equal to evaluation by construction,
|
|
68
|
+
* not by two copies agreeing; every other key stays shallow on both sides.
|
|
69
|
+
*
|
|
70
|
+
* And for exactly ONE value shape, one level deep: an inline locale map
|
|
71
|
+
* (objectui#12936). Each member is evaluated like a plain string on the same
|
|
72
|
+
* key, so each member is scanned like one, through the same recogniser and
|
|
73
|
+
* walker (`localeMap.ts`), and a member whose expression threw is reported by
|
|
74
|
+
* its locale (`properties.title.zh-CN`). Any other object stays unscanned, as
|
|
75
|
+
* it stays unevaluated.
|
|
58
76
|
*/
|
|
59
77
|
/** What counts as an expression — the evaluator's own definition. */
|
|
60
78
|
const EXPRESSION_PATTERN = /\$\{([^}]+)\}/g;
|
|
@@ -78,18 +96,42 @@ export function findExpressionSources(value) {
|
|
|
78
96
|
}
|
|
79
97
|
return found;
|
|
80
98
|
}
|
|
99
|
+
function pushIfUnevaluated(key, channel, value, into) {
|
|
100
|
+
const expressions = findExpressionSources(value);
|
|
101
|
+
if (expressions.length > 0) {
|
|
102
|
+
into.push({ key, channel, value, expressions });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
81
105
|
function scanBag(bag, channel, into, skip) {
|
|
82
|
-
if (!
|
|
106
|
+
if (!isConfigBag(bag))
|
|
83
107
|
return;
|
|
84
108
|
for (const [key, value] of Object.entries(bag)) {
|
|
85
|
-
if (typeof value !== 'string')
|
|
86
|
-
continue;
|
|
87
109
|
if (skip?.(key, value))
|
|
88
110
|
continue;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
111
|
+
// objectui#7867 — `params` is evaluated leaf-deep, so it is scanned
|
|
112
|
+
// leaf-deep, by the evaluator's own walker; the finding's `key` is the
|
|
113
|
+
// leaf's path (`params.target.id`). The visitor hands every leaf back
|
|
114
|
+
// unchanged, so the walk allocates nothing.
|
|
115
|
+
if (key === PARAMS_KEY && isParamsBag(value)) {
|
|
116
|
+
mapParamsLeaves(value, (leaf, path) => {
|
|
117
|
+
pushIfUnevaluated(path, channel, leaf, into);
|
|
118
|
+
return leaf;
|
|
119
|
+
});
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
// objectui#12936 — an inline locale map's members are evaluated like plain
|
|
123
|
+
// strings, so they are scanned like them, through the evaluator's own
|
|
124
|
+
// recogniser and walker. The visitor returns each member unchanged.
|
|
125
|
+
if (isInlineLocaleMap(value)) {
|
|
126
|
+
mapLocaleMapMembers(value, (member, locale) => {
|
|
127
|
+
pushIfUnevaluated(key + '.' + locale, channel, member, into);
|
|
128
|
+
return member;
|
|
129
|
+
});
|
|
130
|
+
continue;
|
|
92
131
|
}
|
|
132
|
+
if (typeof value !== 'string')
|
|
133
|
+
continue;
|
|
134
|
+
pushIfUnevaluated(key, channel, value, into);
|
|
93
135
|
}
|
|
94
136
|
}
|
|
95
137
|
/**
|
|
@@ -114,9 +156,7 @@ export function collectUnevaluatedExpressions(spread, properties, props) {
|
|
|
114
156
|
scanBag(properties, 'properties', findings);
|
|
115
157
|
// The hoist copies every `properties.*` value onto the node's top level, so
|
|
116
158
|
// the same residue is visible twice. Report the authored spelling only.
|
|
117
|
-
const hoisted = properties
|
|
118
|
-
? properties
|
|
119
|
-
: undefined;
|
|
159
|
+
const hoisted = isConfigBag(properties) ? properties : undefined;
|
|
120
160
|
scanBag(spread, 'schema', findings, (key, value) => hoisted?.[key] === value);
|
|
121
161
|
scanBag(props, 'props', findings);
|
|
122
162
|
return findings;
|
|
@@ -140,6 +180,44 @@ function locate(finding) {
|
|
|
140
180
|
/**
|
|
141
181
|
* Build the message. Separate from the emit so a test can assert the words a
|
|
142
182
|
* developer is going to read, not merely that something was logged.
|
|
183
|
+
*
|
|
184
|
+
* ## Why `properties.*` is in the channel list (objectui#7849)
|
|
185
|
+
*
|
|
186
|
+
* It was missing, and the omission CONTRADICTED the sibling diagnostic in
|
|
187
|
+
* `propsBagDiagnostic.ts`, which tells an author who wrote the same key one
|
|
188
|
+
* envelope out: *"`props` is NOT hoisted onto the node — only `properties.*`
|
|
189
|
+
* is … Write them under `properties` instead."* An author whose node trips
|
|
190
|
+
* both messages was told to use `properties` by one and, by the other, that
|
|
191
|
+
* the only channels that work are `content` or host-side resolution.
|
|
192
|
+
*
|
|
193
|
+
* The enumeration was the wrong half. `properties` is not a channel on its way
|
|
194
|
+
* out and there is no retirement on record for it: `@objectstack/spec@17.2.0`
|
|
195
|
+
* calls the vocabulary carried there *"ALIVE — this is not dead surface to
|
|
196
|
+
* retire under ADR-0049"*, keeps `PageComponentSchema.properties` as the open
|
|
197
|
+
* carrier ON PURPOSE, gates it at the authoring door
|
|
198
|
+
* (`validate-component-props.ts`), and tombstones no part of it via
|
|
199
|
+
* `retiredKey()`. In THIS repo `props` — not `properties` — is the spelling
|
|
200
|
+
* annotated as the legacy alias (`SchemaRenderer.tsx`: "the legacy `props`
|
|
201
|
+
* bag, minus every key the canonical `properties` bag also …").
|
|
202
|
+
*
|
|
203
|
+
* Measured on this branch's base through the real renderers, not inferred:
|
|
204
|
+
* `badge` with `properties.label = '${data.status}'` renders `completed` and
|
|
205
|
+
* applies the variant classes; `card` with a conditional
|
|
206
|
+
* `properties.className` renders `class="… border-red-500"`; the same keys
|
|
207
|
+
* under `props` render an EMPTY badge body and no `border-red-500`.
|
|
208
|
+
*
|
|
209
|
+
* The words are borrowed from the sibling message on purpose — one hoist, one
|
|
210
|
+
* vocabulary, so the two diagnostics cannot drift apart again. Pinned by
|
|
211
|
+
* `__tests__/diagnosticChannelConsistency.test.ts`.
|
|
212
|
+
*
|
|
213
|
+
* ## Why `params` is in it too (objectui#7867)
|
|
214
|
+
*
|
|
215
|
+
* The same defect #7849 closed, one channel later: once every string leaf of a
|
|
216
|
+
* `params` bag evaluates, an enumeration that left `params` out would tell an
|
|
217
|
+
* author whose `params.recordId` template THREW that `params` is not a channel
|
|
218
|
+
* at all — and send them to move a correct key somewhere else. A finding on a
|
|
219
|
+
* `params` leaf means the second sentence above (the expression threw, or the
|
|
220
|
+
* value it produced still reads as a template), never the first.
|
|
143
221
|
*/
|
|
144
222
|
export function formatUnevaluatedExpressionMessage(type, id, findings) {
|
|
145
223
|
const node = typeof type === 'string' && type ? '"' + type + '"' : '(untyped node)';
|
|
@@ -155,8 +233,11 @@ export function formatUnevaluatedExpressionMessage(type, id, findings) {
|
|
|
155
233
|
'to the DOM, with its source text intact. Either SchemaRenderer does not\n' +
|
|
156
234
|
'evaluate this key, or the expression threw and the evaluator returned the\n' +
|
|
157
235
|
'source unchanged.\n' +
|
|
158
|
-
'Channels that do evaluate and read back today: `content
|
|
159
|
-
'
|
|
236
|
+
'Channels that do evaluate and read back today: `content`; `properties.*`,\n' +
|
|
237
|
+
'which is evaluated and then HOISTED onto the node, so a renderer declared\n' +
|
|
238
|
+
'as `({ schema })` reads it back as `schema.<key>`; every string leaf of a\n' +
|
|
239
|
+
'`params` bag, at any depth, node-level or under `properties`; or resolve\n' +
|
|
240
|
+
'the value in the host before handing the schema to SchemaRenderer.');
|
|
160
241
|
}
|
|
161
242
|
/**
|
|
162
243
|
* Reported nodes, so a re-render (or the post-mount forceUpdate that picks up
|
|
@@ -6,14 +6,16 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Diagnostic: a node-gate predicate could not be evaluated (objectui#5454,
|
|
10
|
+
* leg 3 of the 2026-08-21 ruling; production coverage added by objectui#6038,
|
|
11
|
+
* maintainer ruling 2026-08-25 option B; the `disabled` / `disabledOn` gate
|
|
12
|
+
* added by objectui#6445).
|
|
11
13
|
*
|
|
12
14
|
* ## The defect this names
|
|
13
15
|
*
|
|
14
16
|
* `SchemaRenderer`'s visibility chain is FAIL-SOFT: `evaluateCondition` answers
|
|
15
|
-
* an unevaluable predicate with `true`. On the
|
|
16
|
-
* `visibleWhen` / `visibleOn`
|
|
17
|
+
* an unevaluable predicate with `true`. On the negated legs (`visible` /
|
|
18
|
+
* `visibleWhen` / `visibleOn`) that `true` means SHOWN — so "this
|
|
17
19
|
* predicate is broken" and "this predicate said yes" reach the screen as the
|
|
18
20
|
* same pixel. An author who mistyped a root, or who wrote a `record.*` gate
|
|
19
21
|
* before objectui#5454 bound the row, saw their block render and had no signal
|
|
@@ -30,9 +32,11 @@
|
|
|
30
32
|
* It changes NO verdict. `evaluateCondition` already returned `true` for every
|
|
31
33
|
* unevaluable predicate on every one of its paths, and the caller reproduces
|
|
32
34
|
* exactly that from its catch — including on the two NON-negated legs
|
|
33
|
-
* (`hidden` / `hiddenOn`), where the same `true` means HIDE
|
|
34
|
-
*
|
|
35
|
-
*
|
|
35
|
+
* (`hidden` / `hiddenOn`), where the same `true` means HIDE — and, since
|
|
36
|
+
* objectui#6445, on the `disabled` / `disabledOn` gate, where it means GREYED
|
|
37
|
+
* OUT. Flipping fail-soft to fail-closed is a shipped-behaviour change tracked
|
|
38
|
+
* separately upstream (objectstack#5149, appeal 1, undecided); this module is
|
|
39
|
+
* the diagnostic half only, on every gate wired to it.
|
|
36
40
|
*
|
|
37
41
|
* ## Why a separate module
|
|
38
42
|
*
|
|
@@ -48,19 +52,208 @@
|
|
|
48
52
|
* and so an app can filter it out of its console transport if it must.
|
|
49
53
|
*/
|
|
50
54
|
export declare const UNRESOLVABLE_VISIBILITY_PREFIX = "[ObjectUI] A visibility predicate could not be evaluated";
|
|
55
|
+
/**
|
|
56
|
+
* The same opening line for the ENABLEMENT gate (`disabled` / `disabledOn`,
|
|
57
|
+
* objectui#6445). A sibling constant rather than a widened one: the six
|
|
58
|
+
* visibility legs keep the bytes they ship today (objectui#6487 pinned them),
|
|
59
|
+
* and calling a `disabled` predicate a "visibility predicate" would send an
|
|
60
|
+
* author to the wrong gate on the first line - the one line a console filter
|
|
61
|
+
* and a `grep` both read.
|
|
62
|
+
*
|
|
63
|
+
* Same posture as its sibling in every other respect: same reporter, same
|
|
64
|
+
* severity, same dedupe `Set`, same key shape, same test-only reset. Only the
|
|
65
|
+
* words that would be FALSE on this gate differ.
|
|
66
|
+
*/
|
|
67
|
+
export declare const UNRESOLVABLE_ENABLEMENT_PREFIX = "[ObjectUI] An enablement predicate could not be evaluated";
|
|
68
|
+
/**
|
|
69
|
+
* Which SCOPE the faulting predicate was evaluated against (objectui#6487).
|
|
70
|
+
*
|
|
71
|
+
* ## Why the reporter needs to be told, rather than deducing it
|
|
72
|
+
*
|
|
73
|
+
* The closing paragraph of the message names the ROOTS an author can bind. That
|
|
74
|
+
* is the half of the report an author acts on: a fault is usually a mistyped or
|
|
75
|
+
* unbound root, and "which roots exist here" is the answer. It is therefore the
|
|
76
|
+
* one part of the copy that is NOT true on every surface — the callers below
|
|
77
|
+
* build genuinely different bags — and it was printed unconditionally in the
|
|
78
|
+
* node tier's spelling, so an app-shell author was sent to check `record` and
|
|
79
|
+
* `page.<var>`, which nothing at that tier binds.
|
|
80
|
+
*
|
|
81
|
+
* The alternative fix — generalising the copy to "whatever this surface binds"
|
|
82
|
+
* plus a docs link — was refused deliberately: the concrete root names are the
|
|
83
|
+
* whole value of the paragraph, and advice that names no roots is correct
|
|
84
|
+
* everywhere and useful nowhere.
|
|
85
|
+
*
|
|
86
|
+
* Deducing the tier from the `type` slot was refused too. `type` carries the
|
|
87
|
+
* SCHEMA NODE TYPE at the node tier and a surface label at the other two
|
|
88
|
+
* (`page:tabs`, `app-shell:visible`), and `page:tabs` is a real registry key —
|
|
89
|
+
* so a `page:tabs` NODE faulting its own `visibleWhen` inside `SchemaRenderer`
|
|
90
|
+
* is indistinguishable, by `type` alone, from one of its ITEM predicates. An
|
|
91
|
+
* explicit argument cannot collide that way.
|
|
92
|
+
*
|
|
93
|
+
* ## The tiers, each derived from the code that builds the bag
|
|
94
|
+
*
|
|
95
|
+
* `'page-component'` — the node gate. `SchemaRenderer.tsx` builds
|
|
96
|
+
* `new ExpressionEvaluator({ ...predicateScope, current_user, [record], data,
|
|
97
|
+
* page })`, so the roots the SPEC declares for the tier (`ui/page.zod.ts`:
|
|
98
|
+
* *"Binds `record`, `current_user`, `page.<var>`"*) are the roots it binds.
|
|
99
|
+
* `page:tabs` ITEM predicates (`containers.tsx`) are this tier as well: they
|
|
100
|
+
* build the same three roots (plus the row spread flat, and `data` aliased to
|
|
101
|
+
* the row rather than to the adapter — both undeclared breadth that the spec
|
|
102
|
+
* does not promise and this paragraph therefore does not advertise).
|
|
103
|
+
*
|
|
104
|
+
* `'app-shell'` — the chrome gate. `ExpressionProvider.tsx` builds
|
|
105
|
+
* `{ current_user, user, ctx: { user }, os: { user }, features }`.
|
|
106
|
+
* There is no `record` and no `page` in it at all, which is the defect this
|
|
107
|
+
* type exists to fix. The four identity spellings are the ADR-0068 aliases and
|
|
108
|
+
* all four resolve; `features` is the deployment-flag root that provider's own
|
|
109
|
+
* docblock documents for exactly this kind of predicate.
|
|
110
|
+
*
|
|
111
|
+
* ⛔ No `app`. It was in that bag and in the paragraph below until
|
|
112
|
+
* objectui#8155 (ruled 2026-09-07): neither ADR-0068 nor
|
|
113
|
+
* `@objectstack/formula`'s `SCOPE_ROOTS` declares such a root, so
|
|
114
|
+
* `buildExpressionScope` stopped binding it. Naming it here would be worse
|
|
115
|
+
* than stale — this paragraph is printed at the exact moment a saved `app.*`
|
|
116
|
+
* predicate faults, so it would answer "why did my predicate not resolve?"
|
|
117
|
+
* with the root that is the reason.
|
|
118
|
+
*
|
|
119
|
+
* ⛔ No `data` either, since objectui#8166 (ruled 2026-09-10). It WAS bound at
|
|
120
|
+
* the app-shell tier and was already withheld from the paragraph below, on the
|
|
121
|
+
* grounds that every `ExpressionProvider` mount passes `data={{}}` — but
|
|
122
|
+
* `AppContent`'s own field-list evaluator passed the record under edit, so on
|
|
123
|
+
* that one leg an authored `data.*` resolved silently against the host's record
|
|
124
|
+
* instead of faulting. `buildExpressionScope` stopped binding it; a `data.*`
|
|
125
|
+
* predicate at this tier now reaches this reporter with the engine's
|
|
126
|
+
* `Unknown variable: data`, the same verdict the server gives it.
|
|
127
|
+
*/
|
|
128
|
+
export type PredicateScopeTier = 'page-component' | 'app-shell';
|
|
129
|
+
/**
|
|
130
|
+
* WHICH GATE the faulting predicate was authored on (objectui#6445).
|
|
131
|
+
*
|
|
132
|
+
* ## Why the reporter has to be told, when it already prints the key
|
|
133
|
+
*
|
|
134
|
+
* `evaluateCondition` answers an unevaluable predicate with `true` on every one
|
|
135
|
+
* of its paths, and every caller reproduces that answer. What that `true` DOES
|
|
136
|
+
* to the node is not a property of the predicate - it is a property of the gate
|
|
137
|
+
* it was authored on, and the consequence paragraph has to say which:
|
|
138
|
+
*
|
|
139
|
+
* `'visibility'` - the visibility chain. Its `true` means SHOWN on the
|
|
140
|
+
* negated legs, so the safe default does NOT bite: a predicate that could not be
|
|
141
|
+
* evaluated reads on screen exactly like one that said yes, which is why the
|
|
142
|
+
* console line is the only signal an author gets.
|
|
143
|
+
*
|
|
144
|
+
* `'enablement'` - `disabled` / `disabledOn`. The SAME `true` GREYS THE CONTROL
|
|
145
|
+
* OUT, so here the safe default is the one that BITES. The author is looking at
|
|
146
|
+
* a control they can see and cannot use; telling them the gate did not bite
|
|
147
|
+
* would be the opposite of what is on their screen, and would send them looking
|
|
148
|
+
* for a rendering bug rather than at their own predicate.
|
|
149
|
+
*
|
|
150
|
+
* ## Why a parameter, and not deduced from `key`
|
|
151
|
+
*
|
|
152
|
+
* Same reason {@link PredicateScopeTier} is a parameter (read its docblock): the
|
|
153
|
+
* caller knows, and a deduction is a table that goes quietly wrong the moment a
|
|
154
|
+
* spelling arrives that it has not heard of - a new alias, or a caller outside
|
|
155
|
+
* this repo - which would inherit a consequence sentence written about some
|
|
156
|
+
* other gate. That is the exact defect class this card and objectui#6487 both
|
|
157
|
+
* exist to remove, so it is not worth re-introducing to save an argument.
|
|
158
|
+
*
|
|
159
|
+
* `'concealment'` - `hidden` / `hiddenOn` (objectui#6503). These are visibility
|
|
160
|
+
* legs, but the only two whose verdict `SchemaRenderer` does NOT negate, so the
|
|
161
|
+
* SAME `true` that SHOWS a node on the four negated legs REMOVES it here. They
|
|
162
|
+
* resolved to `'visibility'` until objectui#6503 and were therefore handed the
|
|
163
|
+
* one sentence that is false about them: the gate did not merely bite, it bit
|
|
164
|
+
* hardest of the three, and the node is not on screen to be inspected at all.
|
|
165
|
+
* This is the third member objectui#6445's docblock predicted, in the shape it
|
|
166
|
+
* predicted - a table entry and a routing decision, no signature change.
|
|
167
|
+
*
|
|
168
|
+
* ## Why a third MEMBER, and not `'visibility'` plus a `negated` flag
|
|
169
|
+
*
|
|
170
|
+
* What varies is the consequence sentence, and it varies per GATE - not along
|
|
171
|
+
* an independent axis a caller could set to contradict the gate it passed
|
|
172
|
+
* alongside. Three flat members cannot be spelled inconsistently; a
|
|
173
|
+
* `(kind, negated)` pair can, and `('enablement', negated: true)` would name a
|
|
174
|
+
* combination no renderer in this repo produces. The table stays the one place
|
|
175
|
+
* that decides what a fail-soft default DID.
|
|
176
|
+
*
|
|
177
|
+
* ## What widening this union costs, stated because it is a published surface
|
|
178
|
+
*
|
|
179
|
+
* `PredicateGateKind` is re-exported from `packages/react/src/index.ts` - the
|
|
180
|
+
* package entry, and the chain stops there (no sibling package re-exports it,
|
|
181
|
+
* and no file outside `packages/react` names it). A consumer switching
|
|
182
|
+
* EXHAUSTIVELY over the union, or holding a `Record<PredicateGateKind, ...>`,
|
|
183
|
+
* gains a case to answer. That is a type-level change only: no runtime
|
|
184
|
+
* signature moved, and every value that was accepted before still is.
|
|
185
|
+
*
|
|
186
|
+
* `'withholding'` - the app-shell's navigation `visible` gate (objectui#12700).
|
|
187
|
+
* A visibility predicate on a navigation entry, but one whose FAULT is answered
|
|
188
|
+
* fail-CLOSED: the shell withholds an entry whose gate it cannot evaluate (most
|
|
189
|
+
* often a `current_user.can(...)` asked before the permissions payload is
|
|
190
|
+
* there) instead of drawing it for everyone. So the default BITES, and the
|
|
191
|
+
* sentence `'visibility'` prints - "reads on screen exactly like one that said
|
|
192
|
+
* yes" - would be the opposite of what the member sees. Same opening line as
|
|
193
|
+
* `'visibility'`, for the same reason `'concealment'` keeps it.
|
|
194
|
+
*/
|
|
195
|
+
export type PredicateGateKind = 'visibility' | 'enablement' | 'concealment' | 'withholding';
|
|
51
196
|
/**
|
|
52
197
|
* Build the message. Separate from the emit so a test can assert the words,
|
|
53
198
|
* not merely that something was logged.
|
|
199
|
+
*
|
|
200
|
+
* `tier` defaults to `'page-component'` and `gate` to `'visibility'` so the
|
|
201
|
+
* historical five-argument call keeps printing the exact bytes it printed
|
|
202
|
+
* before (objectui#6487, objectui#6445) — the defaults are a compatibility
|
|
203
|
+
* shim for callers outside this repo, not something this repo leans on: every
|
|
204
|
+
* in-repo call site passes both explicitly, which is what makes each one a
|
|
205
|
+
* stated decision rather than an inherited one.
|
|
54
206
|
*/
|
|
55
|
-
export declare function formatUnresolvableVisibilityMessage(type: unknown, id: unknown, key: string, raw: unknown, reason: string): string;
|
|
207
|
+
export declare function formatUnresolvableVisibilityMessage(type: unknown, id: unknown, key: string, raw: unknown, reason: string, tier?: PredicateScopeTier, gate?: PredicateGateKind): string;
|
|
56
208
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* the
|
|
60
|
-
*
|
|
61
|
-
*
|
|
209
|
+
* Reports a NODE-GATE predicate that could not be evaluated — in DEVELOPMENT
|
|
210
|
+
* AND IN PRODUCTION since objectui#6038 (maintainer ruling 2026-08-25, option
|
|
211
|
+
* B: "the silence is no longer an accepted property").
|
|
212
|
+
*
|
|
213
|
+
* The visibility chain was the first gate wired to it and is still the default
|
|
214
|
+
* {@link PredicateGateKind}; `disabled` / `disabledOn` joined it in
|
|
215
|
+
* objectui#6445, which is why the name is now narrower than the function. It is
|
|
216
|
+
* kept anyway: this symbol is exported from the package entry and called from
|
|
217
|
+
* `@object-ui/app-shell` and `@object-ui/components`, so renaming it would be a
|
|
218
|
+
* cross-package edit that changes no behaviour, on a card scoped to one file's
|
|
219
|
+
* wiring. What has to stay true is the posture the name once described — ONE
|
|
220
|
+
* reporter, ONE dedupe `Set`, ONE severity, ONE reset — and a second reporter
|
|
221
|
+
* for the second gate is exactly what that rules out.
|
|
222
|
+
*
|
|
223
|
+
* `console.warn`, not `error`: the verdict is unchanged and the page still
|
|
224
|
+
* renders, so this is a diagnostic about a predicate — not the refusal
|
|
225
|
+
* `reportUnevaluatedExpressions` emits once raw source has reached the DOM.
|
|
226
|
+
*
|
|
227
|
+
* ## `err` takes a reason, not only an `Error`
|
|
228
|
+
*
|
|
229
|
+
* The dev caller catches a throw and passes the `Error`; the production caller
|
|
230
|
+
* is handed the evaluator's own reason STRING through `EvaluationOptions.onFault`
|
|
231
|
+
* (no throw is raised there, because raising one would cost a second
|
|
232
|
+
* evaluation). `String(err)` already covered that shape, so both callers reach
|
|
233
|
+
* the same `Reason:` text and the same dedupe entry — which is what makes "dev
|
|
234
|
+
* and production print the identical line" true rather than approximately true.
|
|
235
|
+
*
|
|
236
|
+
* ## `tier` is NOT in the dedupe key, and that is not an oversight
|
|
237
|
+
*
|
|
238
|
+
* The key stays `(type, key, predicate source)` — the rate limit objectui#6038
|
|
239
|
+
* pinned in both directions. Adding `tier` could only ever LOOSEN it, and it
|
|
240
|
+
* would take a `type` shared across two tiers to loosen anything: the app-shell
|
|
241
|
+
* site's `type` is the constant `'app-shell:visible'`, which no node tier can
|
|
242
|
+
* produce, and the two `'page-component'` callers are the same tier by
|
|
243
|
+
* definition. So the tier is free of the key by measurement, not by assumption.
|
|
244
|
+
*
|
|
245
|
+
* ## Neither is `gate`, and for a stronger reason (objectui#6445)
|
|
246
|
+
*
|
|
247
|
+
* `gate` is a FUNCTION of `key`, which is already in the dedupe key: the
|
|
248
|
+
* enablement gate is exactly `disabled` / `disabledOn` and the visibility gate
|
|
249
|
+
* is exactly the six legs of the visibility chain, two disjoint sets. So adding
|
|
250
|
+
* it could not separate two entries that `key` does not already separate, and
|
|
251
|
+
* the cross-gate direction is pinned rather than argued: the SAME predicate
|
|
252
|
+
* source authored on `disabled` and on `visibleWhen` produces TWO lines, not
|
|
253
|
+
* one - the new reporting site cannot be swallowed by a visibility entry, and
|
|
254
|
+
* cannot swallow one.
|
|
62
255
|
*/
|
|
63
|
-
export declare function reportUnresolvableVisibilityPredicate(type: unknown, id: unknown, key: string, raw: unknown, err: unknown): void;
|
|
256
|
+
export declare function reportUnresolvableVisibilityPredicate(type: unknown, id: unknown, key: string, raw: unknown, err: unknown, tier?: PredicateScopeTier, gate?: PredicateGateKind): void;
|
|
64
257
|
/**
|
|
65
258
|
* Test-only reset for the dedupe above — for BOTH legs, which is the point of
|
|
66
259
|
* their sharing one `Set`. The `Set` is module state: without this, the second
|
|
@@ -76,16 +269,66 @@ export declare function __resetVisibilityPredicateWarnings(): void;
|
|
|
76
269
|
* dev-only unresolvable-predicate report". What is load-bearing there — and
|
|
77
270
|
* what the dispatch restated — is the reporter's POSTURE: same module, same
|
|
78
271
|
* severity (`console.warn`), same dedupe key shape, same dedupe Set, same
|
|
79
|
-
*
|
|
80
|
-
*
|
|
272
|
+
* gate at the same call site, same test-only reset. All of that is shared
|
|
273
|
+
* below.
|
|
274
|
+
*
|
|
275
|
+
* ⚠️ The two legs stopped sharing that gate's VALUE in objectui#6038, and only
|
|
276
|
+
* that. The unresolvable leg now reports in production as well, because the
|
|
277
|
+
* 2026-08-25 ruling retired its silence; THIS leg stays dev-only under its own
|
|
278
|
+
* 2026-08-22 ruling, and the difference is not an oversight. A fault is a
|
|
279
|
+
* predicate that could not be evaluated, and shipping a live gate that has
|
|
280
|
+
* stopped biting is the class-1 defect production has to be able to see. This
|
|
281
|
+
* leg reports something else: a predicate that evaluated perfectly, against the
|
|
282
|
+
* wrong object. Its trigger is a LEXICAL scan of the predicate source with a
|
|
283
|
+
* stated false-positive residue (the deliberate-absence idioms below), which is
|
|
284
|
+
* a cost worth paying for an author at their keyboard and not for every user of
|
|
285
|
+
* every production page. The first LINE is not reused, because on this path it would
|
|
81
286
|
* state something false: the predicate did not fail to evaluate. It evaluated
|
|
82
287
|
* perfectly, against the wrong object, and produced a constant. Telling an
|
|
83
288
|
* author "could not be evaluated" would send them hunting for a syntax error
|
|
84
289
|
* that is not there.
|
|
85
290
|
*/
|
|
86
|
-
export declare const ADAPTER_ONLY_DATA_PREDICATE_PREFIX = "[ObjectUI] A visibility predicate
|
|
87
|
-
/**
|
|
88
|
-
|
|
291
|
+
export declare const ADAPTER_ONLY_DATA_PREDICATE_PREFIX = "[ObjectUI] A visibility predicate read `data.*` that the bound `data` does not answer";
|
|
292
|
+
/**
|
|
293
|
+
* The `disabled` / `disabledOn` sibling of the prefix above (objectui#6504,
|
|
294
|
+
* maintainer ruling 2026-08-27 option A). Same reason
|
|
295
|
+
* {@link UNRESOLVABLE_ENABLEMENT_PREFIX} is a sibling of
|
|
296
|
+
* {@link UNRESOLVABLE_VISIBILITY_PREFIX} rather than a reuse: calling a
|
|
297
|
+
* `disabled` predicate "a visibility predicate" would send an author to the
|
|
298
|
+
* wrong gate on the first line — the one line a console filter and a `grep`
|
|
299
|
+
* both read.
|
|
300
|
+
*/
|
|
301
|
+
export declare const ADAPTER_ONLY_ENABLEMENT_PREDICATE_PREFIX = "[ObjectUI] An enablement predicate read `data.*` that the bound `data` does not answer";
|
|
302
|
+
/**
|
|
303
|
+
* Which gates the objectui#5687 constant-predicate diagnostic is wired to
|
|
304
|
+
* (objectui#6504, maintainer ruling 2026-08-27 option A: extend to the
|
|
305
|
+
* enablement gate, dev-only; option C — always-on — was excluded, outside
|
|
306
|
+
* the #5687 precedent).
|
|
307
|
+
*
|
|
308
|
+
* A NARROWER union than {@link PredicateGateKind} deliberately, not an
|
|
309
|
+
* oversight: `'concealment'` (`hidden` / `hiddenOn`) is not one of them. Both
|
|
310
|
+
* of this diagnostic's call sites are unchanged by this card — the visibility
|
|
311
|
+
* one (`SchemaRenderer.tsx`'s `evaluateVisibilityPredicate`) still calls
|
|
312
|
+
* {@link reportAdapterOnlyDataPredicate} with no `gate` argument at all, for
|
|
313
|
+
* the six-leg chain exactly as objectui#5687 shipped it, so `'concealment'`
|
|
314
|
+
* was never a value this table had to answer for. Giving it an entry nobody
|
|
315
|
+
* can reach would be a decision this ruling did not make; widening this type
|
|
316
|
+
* later is a decision for whichever card makes that call, not a `??` for this
|
|
317
|
+
* table to paper over now.
|
|
318
|
+
*/
|
|
319
|
+
export type AdapterOnlyPredicateGateKind = Extract<PredicateGateKind, 'visibility' | 'enablement'>;
|
|
320
|
+
/**
|
|
321
|
+
* Built separately from the emit, for the same reason as the message above.
|
|
322
|
+
*
|
|
323
|
+
* `gate` defaults to `'visibility'` so the historical four-argument call
|
|
324
|
+
* (objectui#5687) keeps printing the exact bytes it printed before —
|
|
325
|
+
* objectui#6504's own call site in `evaluateVisibilityPredicate`
|
|
326
|
+
* (`SchemaRenderer.tsx`) is one of those unchanged callers, passing no `gate`
|
|
327
|
+
* at all. `evaluateEnablementPredicate` is the one caller that states
|
|
328
|
+
* `'enablement'` explicitly, matching {@link formatUnresolvableVisibilityMessage}'s
|
|
329
|
+
* own compatibility-default pattern.
|
|
330
|
+
*/
|
|
331
|
+
export declare function formatAdapterOnlyDataMessage(type: unknown, id: unknown, key: string, raw: unknown, unresolved: string[], gate?: AdapterOnlyPredicateGateKind): string;
|
|
89
332
|
/**
|
|
90
333
|
* Dev-build diagnostic for a node-gate predicate that reads `data.*` and gets
|
|
91
334
|
* `undefined` back from the adapter (objectui#5687, maintainer ruling
|
|
@@ -111,9 +354,27 @@ export declare function formatAdapterOnlyDataMessage(type: unknown, id: unknown,
|
|
|
111
354
|
* objectui#5454 existed to remove, one path further along.
|
|
112
355
|
*
|
|
113
356
|
* Sharing the module means sharing the LIFECYCLE, which is the part that has to
|
|
114
|
-
* match: one dedupe Set, one reset, one severity
|
|
357
|
+
* match: one dedupe Set, one reset, one severity. (One gate, too, until
|
|
358
|
+
* objectui#6038 — see the prefix constant above for why only the sibling leg
|
|
359
|
+
* crossed into production.) The dedupe
|
|
115
360
|
* key is tagged with this leg's name so the two diagnostics cannot silence each
|
|
116
361
|
* other for the same (type, key, source) triple — they are different faults,
|
|
117
362
|
* and a node that faults one way is not evidence about the other.
|
|
363
|
+
*
|
|
364
|
+
* ## `gate` (objectui#6504) — NOT in the dedupe key, for the reason
|
|
365
|
+
* {@link reportUnresolvableVisibilityPredicate}'s own docblock gives for the
|
|
366
|
+
* same omission
|
|
367
|
+
*
|
|
368
|
+
* `gate` is a FUNCTION of `key`: the enablement gate is exactly `disabled` /
|
|
369
|
+
* `disabledOn` and the visibility gate this leg is called for is the six-leg
|
|
370
|
+
* chain, two disjoint sets. Adding it to the dedupe key could not separate two
|
|
371
|
+
* entries `key` does not already separate — and the cross-gate direction is
|
|
372
|
+
* the one that would matter if it could: the SAME predicate source authored on
|
|
373
|
+
* `disabled` and on (say) `visibleWhen` must produce TWO lines, which it does
|
|
374
|
+
* today because `key` differs, with or without `gate` in the tuple.
|
|
375
|
+
*
|
|
376
|
+
* Defaults to `'visibility'` — the objectui#5687 call site
|
|
377
|
+
* (`evaluateVisibilityPredicate`) is unchanged by this card and still calls
|
|
378
|
+
* this function with five arguments.
|
|
118
379
|
*/
|
|
119
|
-
export declare function reportAdapterOnlyDataPredicate(type: unknown, id: unknown, key: string, raw: unknown, boundData: unknown): void;
|
|
380
|
+
export declare function reportAdapterOnlyDataPredicate(type: unknown, id: unknown, key: string, raw: unknown, boundData: unknown, gate?: AdapterOnlyPredicateGateKind): void;
|