@object-ui/core 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 +7260 -0
- package/README.md +228 -14
- package/dist/actions/ActionEngine.d.ts +4 -2
- package/dist/actions/ActionEngine.js +13 -8
- package/dist/actions/ActionRunner.d.ts +473 -69
- package/dist/actions/ActionRunner.js +792 -125
- package/dist/actions/TransactionManager.d.ts +7 -2
- package/dist/actions/UndoManager.d.ts +8 -13
- package/dist/actions/actionKeys.d.ts +9 -7
- package/dist/actions/actionKeys.js +67 -14
- package/dist/actions/actionResponse.js +2 -2
- package/dist/actions/index.d.ts +2 -0
- package/dist/actions/index.js +2 -0
- package/dist/actions/navigationTarget.d.ts +41 -0
- package/dist/actions/navigationTarget.js +20 -0
- package/dist/actions/recordDelete.d.ts +154 -0
- package/dist/actions/recordDelete.js +110 -0
- package/dist/adapters/ApiDataSource.d.ts +8 -4
- package/dist/adapters/ApiDataSource.js +19 -7
- package/dist/adapters/ValueDataSource.d.ts +6 -1
- package/dist/adapters/ValueDataSource.js +1090 -98
- package/dist/adapters/batchTransaction.d.ts +1 -1
- package/dist/adapters/batchTransaction.js +1 -1
- package/dist/adapters/resolveDataSource.d.ts +9 -6
- package/dist/adapters/resolveDataSource.js +9 -12
- package/dist/builder/schema-builder.d.ts +38 -81
- package/dist/builder/schema-builder.js +33 -152
- package/dist/data-scope/ViewDataProvider.d.ts +8 -25
- package/dist/data-scope/ViewDataProvider.js +27 -54
- package/dist/data-scope/element-data-source.d.ts +261 -7
- package/dist/data-scope/element-data-source.js +342 -52
- package/dist/data-scope/index.d.ts +9 -4
- package/dist/data-scope/index.js +9 -4
- package/dist/evaluator/ExpressionEvaluator.d.ts +86 -0
- package/dist/evaluator/ExpressionEvaluator.js +184 -12
- package/dist/evaluator/FormulaFunctions.d.ts +0 -10
- package/dist/evaluator/FormulaFunctions.js +166 -27
- package/dist/evaluator/deadlineSettled.d.ts +40 -0
- package/dist/evaluator/deadlineSettled.js +45 -0
- package/dist/evaluator/declaredPredicate.d.ts +93 -9
- package/dist/evaluator/declaredPredicate.js +117 -16
- package/dist/evaluator/fieldRules.d.ts +88 -4
- package/dist/evaluator/fieldRules.js +208 -31
- package/dist/evaluator/index.d.ts +2 -0
- package/dist/evaluator/index.js +2 -0
- package/dist/evaluator/listConditional.d.ts +103 -34
- package/dist/evaluator/listConditional.js +127 -50
- package/dist/evaluator/optionRules.d.ts +77 -6
- package/dist/evaluator/optionRules.js +87 -10
- package/dist/evaluator/predicateInput.d.ts +15 -4
- package/dist/evaluator/predicateInput.js +35 -2
- package/dist/evaluator/rowPredicateCanon.d.ts +7 -18
- package/dist/evaluator/rowPredicateCanon.js +65 -115
- package/dist/evaluator/subjectPermissions.d.ts +105 -0
- package/dist/evaluator/subjectPermissions.js +86 -0
- package/dist/evaluator/unevaluablePredicate.d.ts +57 -0
- package/dist/evaluator/unevaluablePredicate.js +70 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +83 -2
- package/dist/protocols/index.d.ts +0 -1
- package/dist/protocols/index.js +9 -1
- package/dist/registry/Registry.d.ts +382 -34
- package/dist/registry/Registry.js +247 -12
- package/dist/registry/WidgetRegistry.js +7 -3
- package/dist/registry/html-tier-intrinsics.d.ts +90 -0
- package/dist/registry/html-tier-intrinsics.js +110 -0
- package/dist/registry/public-blocks.d.ts +8 -0
- package/dist/registry/public-blocks.js +57 -0
- package/dist/theme/ThemeEngine.js +1 -1
- package/dist/utils/affordanceGrants.d.ts +371 -0
- package/dist/utils/affordanceGrants.js +252 -0
- package/dist/utils/chart-category-key.d.ts +82 -0
- package/dist/utils/chart-category-key.js +84 -0
- package/dist/utils/chart-measure-key.d.ts +64 -0
- package/dist/utils/chart-measure-key.js +65 -0
- package/dist/utils/chart-presentation.d.ts +95 -112
- package/dist/utils/chart-presentation.js +169 -104
- package/dist/utils/chart-series.d.ts +165 -5
- package/dist/utils/chart-series.js +123 -18
- package/dist/utils/column-sortability.d.ts +157 -0
- package/dist/utils/column-sortability.js +217 -0
- package/dist/utils/dashboard-filters.d.ts +38 -6
- package/dist/utils/dashboard-filters.js +107 -61
- package/dist/utils/dataset-format.d.ts +211 -30
- package/dist/utils/dataset-format.js +389 -46
- package/dist/utils/date-display.d.ts +346 -0
- package/dist/utils/date-display.js +644 -0
- package/dist/utils/date-macros.js +7 -0
- package/dist/utils/drill-down.d.ts +78 -0
- package/dist/utils/drill-down.js +93 -0
- package/dist/utils/emptiness.d.ts +96 -0
- package/dist/utils/emptiness.js +101 -0
- package/dist/utils/expand-fields.d.ts +81 -9
- package/dist/utils/expand-fields.js +81 -9
- package/dist/utils/extract-records.d.ts +65 -5
- package/dist/utils/extract-records.js +65 -8
- package/dist/utils/filter-converter.d.ts +158 -6
- package/dist/utils/filter-converter.js +1098 -24
- package/dist/utils/filter-tokens.d.ts +49 -5
- package/dist/utils/filter-tokens.js +105 -33
- package/dist/utils/grouping-fields.d.ts +61 -0
- package/dist/utils/grouping-fields.js +84 -0
- package/dist/utils/managedBy.d.ts +3 -3
- package/dist/utils/managedBy.js +5 -5
- package/dist/utils/native-date-value.d.ts +48 -0
- package/dist/utils/native-date-value.js +167 -0
- package/dist/utils/non-grid-row-ceiling.d.ts +143 -0
- package/dist/utils/non-grid-row-ceiling.js +141 -0
- package/dist/utils/normalize-list-view.d.ts +135 -11
- package/dist/utils/normalize-list-view.js +243 -64
- package/dist/utils/number-display.d.ts +63 -14
- package/dist/utils/number-display.js +34 -12
- package/dist/utils/object-aggregate-query.d.ts +113 -0
- package/dist/utils/object-aggregate-query.js +50 -0
- package/dist/utils/parent-scope.d.ts +123 -0
- package/dist/utils/parent-scope.js +140 -0
- package/dist/utils/predicate-fields.d.ts +45 -12
- package/dist/utils/predicate-fields.js +141 -12
- package/dist/utils/record-color.d.ts +75 -0
- package/dist/utils/record-color.js +65 -0
- package/dist/utils/record-source.d.ts +298 -0
- package/dist/utils/record-source.js +331 -0
- package/dist/utils/record-title.d.ts +148 -8
- package/dist/utils/record-title.js +220 -26
- package/dist/utils/reference-keys.d.ts +21 -22
- package/dist/utils/reference-keys.js +532 -29
- package/dist/utils/sort-query.d.ts +115 -15
- package/dist/utils/sort-query.js +240 -13
- package/dist/utils/text-comparand.d.ts +92 -0
- package/dist/utils/text-comparand.js +99 -0
- package/dist/utils/without-denied-fields.d.ts +49 -0
- package/dist/utils/without-denied-fields.js +52 -0
- package/dist/validation/index.d.ts +8 -4
- package/dist/validation/index.js +8 -4
- package/dist/validation/schema-validator.d.ts +1 -1
- package/dist/validation/schema-validator.js +225 -83
- package/dist/validation/server-owned-value.d.ts +5 -1
- package/dist/validation/server-owned-value.js +5 -1
- package/dist/validation/validators/object-validation-engine.d.ts +1 -1
- package/dist/validation/validators/object-validation-engine.js +14 -3
- package/package.json +8 -9
- package/dist/data-scope/DataScopeManager.d.ts +0 -127
- package/dist/data-scope/DataScopeManager.js +0 -229
- package/dist/protocols/ResponsiveProtocol.d.ts +0 -73
- package/dist/protocols/ResponsiveProtocol.js +0 -158
- package/dist/validation/validation-engine.d.ts +0 -88
- package/dist/validation/validation-engine.js +0 -434
|
@@ -9,28 +9,19 @@
|
|
|
9
9
|
* @object-ui/core - View Data Provider
|
|
10
10
|
*
|
|
11
11
|
* Resolves @objectstack/spec ViewData discriminated union into
|
|
12
|
-
* a unified data access interface. Supports
|
|
12
|
+
* a unified data access interface. Supports two data modes:
|
|
13
13
|
*
|
|
14
14
|
* 1. 'object' — Metadata-driven via ObjectStack API (object name → fields/records)
|
|
15
|
-
* 2. '
|
|
16
|
-
*
|
|
15
|
+
* 2. 'value' — Static data (pre-loaded items array)
|
|
16
|
+
*
|
|
17
|
+
* The view-level `'api'` mode is retired in `@objectstack/spec`
|
|
18
|
+
* (objectstack#23082, ADR-0049) and has no arm here (objectui#12325): a
|
|
19
|
+
* config that still names it resolves as an unknown provider, with an error.
|
|
17
20
|
*
|
|
18
21
|
* @module data-scope
|
|
19
22
|
* @packageDocumentation
|
|
20
23
|
*/
|
|
21
|
-
import { collectSavedViews, composeElementDataSource, elementDataSourceViewNotFoundMessage, resolveSavedView, } from './element-data-source.js';
|
|
22
|
-
/** Extract records array from various common API response shapes */
|
|
23
|
-
function extractRecords(data) {
|
|
24
|
-
if (Array.isArray(data))
|
|
25
|
-
return data;
|
|
26
|
-
if (Array.isArray(data?.records))
|
|
27
|
-
return data.records;
|
|
28
|
-
if (Array.isArray(data?.data))
|
|
29
|
-
return data.data;
|
|
30
|
-
if (Array.isArray(data?.items))
|
|
31
|
-
return data.items;
|
|
32
|
-
return [];
|
|
33
|
-
}
|
|
24
|
+
import { collectSavedViews, composeElementDataSource, elementDataSourceRefusedLimitMessage, elementDataSourceViewNotFoundMessage, resolveSavedView, } from './element-data-source.js';
|
|
34
25
|
/**
|
|
35
26
|
* Resolves ViewData configuration into actual data via a pluggable fetcher.
|
|
36
27
|
*
|
|
@@ -59,8 +50,6 @@ export class ViewDataProvider {
|
|
|
59
50
|
switch (config.provider) {
|
|
60
51
|
case 'value':
|
|
61
52
|
return this.resolveValue(config);
|
|
62
|
-
case 'api':
|
|
63
|
-
return this.resolveApi(config);
|
|
64
53
|
case 'object':
|
|
65
54
|
return this.resolveObject(config, options);
|
|
66
55
|
default:
|
|
@@ -83,42 +72,6 @@ export class ViewDataProvider {
|
|
|
83
72
|
loading: false,
|
|
84
73
|
};
|
|
85
74
|
}
|
|
86
|
-
/** Resolve API-based data */
|
|
87
|
-
async resolveApi(config) {
|
|
88
|
-
if (!this.fetcher?.fetchUrl) {
|
|
89
|
-
return {
|
|
90
|
-
records: [],
|
|
91
|
-
total: 0,
|
|
92
|
-
provider: 'api',
|
|
93
|
-
loading: false,
|
|
94
|
-
error: 'No fetchUrl implementation available for API data provider',
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
try {
|
|
98
|
-
const data = await this.fetcher.fetchUrl(config.read.url, {
|
|
99
|
-
method: config.read.method,
|
|
100
|
-
headers: config.read.headers,
|
|
101
|
-
});
|
|
102
|
-
// Handle common response shapes
|
|
103
|
-
const records = extractRecords(data);
|
|
104
|
-
const total = data?.total ?? data?.count ?? records.length;
|
|
105
|
-
return {
|
|
106
|
-
records,
|
|
107
|
-
total,
|
|
108
|
-
provider: 'api',
|
|
109
|
-
loading: false,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
return {
|
|
114
|
-
records: [],
|
|
115
|
-
total: 0,
|
|
116
|
-
provider: 'api',
|
|
117
|
-
loading: false,
|
|
118
|
-
error: error.message,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
75
|
/** Resolve object-based data (metadata-driven) */
|
|
123
76
|
async resolveObject(config, options) {
|
|
124
77
|
if (!this.fetcher) {
|
|
@@ -211,6 +164,26 @@ export class ViewDataProvider {
|
|
|
211
164
|
}
|
|
212
165
|
}
|
|
213
166
|
const composed = composeElementDataSource(config, view);
|
|
167
|
+
// The loud half of the row-cap refusal. This method is the consumer that
|
|
168
|
+
// has NO guard of its own — it forwards `limit` straight to
|
|
169
|
+
// `DataFetcher.fetchRecords` — so before the guard a view's `0` / `-10` /
|
|
170
|
+
// `25.5` reached the fetcher verbatim, and nothing anywhere said so. A
|
|
171
|
+
// renderer that receives the same key reports it through its own channel;
|
|
172
|
+
// this path has no renderer, so it reports here, on the same `console.warn`
|
|
173
|
+
// channel those sites use. ⛔ Not an `error`: the refusal is FAIL-SOFT and
|
|
174
|
+
// the records still load, so blanking the result would be a worse outcome
|
|
175
|
+
// than the defect.
|
|
176
|
+
//
|
|
177
|
+
// Both operands of the chain are asked (objectui#10016): a refused binding
|
|
178
|
+
// `limit` is not authored and yields to the view's cap, so it is reported
|
|
179
|
+
// in its own words. The builder is also handed the binding for the VIEW's
|
|
180
|
+
// sentence, because that sentence says the fetch falls back to a default,
|
|
181
|
+
// which is false when the binding's own usable cap is what gets used.
|
|
182
|
+
for (const operand of ['binding', 'view']) {
|
|
183
|
+
const refusedLimit = elementDataSourceRefusedLimitMessage(view, config.view, config.object, config, operand);
|
|
184
|
+
if (refusedLimit)
|
|
185
|
+
console.warn(refusedLimit);
|
|
186
|
+
}
|
|
214
187
|
const fields = Array.isArray(composed.columns)
|
|
215
188
|
? composed.columns.filter((c) => typeof c === 'string' && !!c)
|
|
216
189
|
: undefined;
|
|
@@ -5,6 +5,63 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* `@object-ui/core` — ElementDataSource resolution (objectstack#5576).
|
|
10
|
+
*
|
|
11
|
+
* `PageComponentSchema.dataSource` (the spec's `ElementDataSourceSchema`) is the
|
|
12
|
+
* per-element data binding a page component uses to query a different object
|
|
13
|
+
* than the page's own context:
|
|
14
|
+
*
|
|
15
|
+
* ```json
|
|
16
|
+
* { "object": "account", "view": "hot", "filter": [ … ], "sort": [ … ], "limit": 20 }
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* This module owns the two pure halves of consuming it — telling the METADATA
|
|
20
|
+
* apart from a runtime data-source adapter, and composing a named saved view
|
|
21
|
+
* with the binding's own keys. Both are needed in two places (the render path in
|
|
22
|
+
* `@object-ui/plugin-list`, and `ViewDataProvider.resolveElementDataSource`), so
|
|
23
|
+
* they live here rather than being written twice.
|
|
24
|
+
*
|
|
25
|
+
* ## Composition semantics
|
|
26
|
+
*
|
|
27
|
+
* The spec's own `describe()` text settles the one case that could go either
|
|
28
|
+
* way. `filter` is documented as "**Additional** filter criteria" — additional
|
|
29
|
+
* to what the named view already restricts — so a view filter and a binding
|
|
30
|
+
* filter COMBINE under `and`; the binding can only narrow the view, never widen
|
|
31
|
+
* it. That is also the safe direction for AI-authored metadata: a typo in a
|
|
32
|
+
* per-element filter cannot silently expose rows the saved view excluded.
|
|
33
|
+
*
|
|
34
|
+
* Nothing else in the binding is described as additional, and the remaining keys
|
|
35
|
+
* are single-valued (one ordering, one cap), so for those the rule is **the view
|
|
36
|
+
* provides the baseline and an explicit key overrides it**:
|
|
37
|
+
*
|
|
38
|
+
* | key | source | rule |
|
|
39
|
+
* |------------|-----------------------------------------|-----------------------------|
|
|
40
|
+
* | `object` | binding (required) | binding wins |
|
|
41
|
+
* | `columns` | view only — the binding has no such key | view |
|
|
42
|
+
* | `filter` | view + binding | AND-combined ("additional") |
|
|
43
|
+
* | `sort` | view or binding | binding overrides view |
|
|
44
|
+
* | `limit` | view (`pagination.pageSize`) or binding | usable binding overrides view |
|
|
45
|
+
* | `viewType` | view only | view |
|
|
46
|
+
*
|
|
47
|
+
* Both operands of `limit` carry one extra condition the other keys do not: the
|
|
48
|
+
* destination is declared a POSITIVE INTEGER, so a cap the contract refuses is
|
|
49
|
+
* NOT AUTHORED, whichever operand carried it. One chain, one rule for both: a
|
|
50
|
+
* usable binding cap, else a usable view cap, else none (the consumer's own
|
|
51
|
+
* default). See `savedViewLimit` for why the view's refused cap is dropped
|
|
52
|
+
* rather than clamped or thrown, `bindingLimit` for why a refused binding cap
|
|
53
|
+
* yields to the view's (objectui#10016), and `elementDataSourceRefusedLimitMessage`
|
|
54
|
+
* for the half that tells the author, about either operand.
|
|
55
|
+
*
|
|
56
|
+
* A lone `filter` — only the view has one, or only the binding does — is passed
|
|
57
|
+
* through in the shape it was stored in; only the two-source case is lowered to
|
|
58
|
+
* an ObjectQL AST, because that is what combining requires.
|
|
59
|
+
*
|
|
60
|
+
* `viewType` rides along because `list-view` IS the multi-kind view container
|
|
61
|
+
* (its registry `inputs` enumerate grid/kanban/gallery/…): naming a saved kanban
|
|
62
|
+
* view and rendering a grid would be a silently wrong answer.
|
|
63
|
+
*/
|
|
64
|
+
import type { InjectedComponentInput } from '@object-ui/types';
|
|
8
65
|
/** Sort entry, as both the spec's `SortItem` and objectui's view configs spell it. */
|
|
9
66
|
export interface ElementDataSourceSort {
|
|
10
67
|
field: string;
|
|
@@ -13,11 +70,38 @@ export interface ElementDataSourceSort {
|
|
|
13
70
|
/**
|
|
14
71
|
* Element-level data source — matches `@objectstack/spec` `ElementDataSourceSchema`.
|
|
15
72
|
*
|
|
16
|
-
* `filter`
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
73
|
+
* `filter` has two populations, and since the upstream convergence they differ:
|
|
74
|
+
*
|
|
75
|
+
* - **What an author may write** is the spec's `ViewFilterRule` array alone —
|
|
76
|
+
* `[{ field, operator, value }, …]`. The spec's `filter` doors converged on it
|
|
77
|
+
* (objectui#6206; migration `element-data-source-and-object-block-filter-rule-array`),
|
|
78
|
+
* and `ElementDataSourceSchema` refuses the other two shapes at the door: the
|
|
79
|
+
* MongoDB-style record form (`{ status: 'open' }`) by kind, and an ObjectQL
|
|
80
|
+
* AST tuple array (`[['status', '=', 'open']]`) because each member must be a
|
|
81
|
+
* rule object.
|
|
82
|
+
* - **What a renderer may still receive** is all three. The D2 conversion that
|
|
83
|
+
* migration entry records, `page-component-filter-record-to-rule-array`,
|
|
84
|
+
* rewrites a stored `filter` only where the rule array spells it losslessly —
|
|
85
|
+
* a flat record, an operator object whose operators the rule vocabulary
|
|
86
|
+
* spells, several such keys, or a single-level AST tuple array — on every
|
|
87
|
+
* ObjectStack stored-row read and under `os migrate meta --stored`. It leaves
|
|
88
|
+
* exactly as stored a filter carrying `$and` / `$or` / `$not`; any filter with
|
|
89
|
+
* a part that has no lossless rule spelling (a `null` value, an operator such
|
|
90
|
+
* as `$null` / `$exists` or an AST `like`, an array or object comparand in
|
|
91
|
+
* equality position, an AST `and` / `or` group); and every filter, the
|
|
92
|
+
* binding's included, of a component whose rows are inline
|
|
93
|
+
* (`data: { provider: 'value' }`, a `data` array, or `staticData`). And this
|
|
94
|
+
* renderer is backend-agnostic: it replays no conversion itself, and only
|
|
95
|
+
* ObjectStack's own data-at-rest seams do, so a page that reaches it any other
|
|
96
|
+
* way arrives as it was written. Any of the three shapes may therefore still
|
|
97
|
+
* arrive here, beside the rule array.
|
|
98
|
+
*
|
|
99
|
+
* `filter` is typed `unknown` rather than the spec's `ViewFilterRule[]` because
|
|
100
|
+
* this interface carries the second population, not the first, and
|
|
101
|
+
* {@link mergeFilterNodes} is the single sink that lowers all three shapes.
|
|
102
|
+
* Narrowing the type here would only move the cast, not remove it, and it would
|
|
103
|
+
* not stop a stored record-form filter from arriving. Refusing the retired
|
|
104
|
+
* shapes is the spec schema's job at the authoring door, not this type's.
|
|
21
105
|
*/
|
|
22
106
|
export interface ElementDataSourceConfig {
|
|
23
107
|
object: string;
|
|
@@ -43,7 +127,12 @@ export interface ComposedElementDataSource {
|
|
|
43
127
|
filter?: unknown;
|
|
44
128
|
/** Binding sort if given, else the view's. */
|
|
45
129
|
sort?: unknown;
|
|
46
|
-
/**
|
|
130
|
+
/**
|
|
131
|
+
* The binding's limit if it is one the contract admits, else the view's page
|
|
132
|
+
* size under the same condition. A refused cap from either operand is not
|
|
133
|
+
* authored (`bindingLimit` and `savedViewLimit` apply the one rule), and
|
|
134
|
+
* `elementDataSourceRefusedLimitMessage` is what says so.
|
|
135
|
+
*/
|
|
47
136
|
limit?: number;
|
|
48
137
|
/** The view's render kind (grid / kanban / …), when the view declares one. */
|
|
49
138
|
viewType?: string;
|
|
@@ -91,12 +180,65 @@ export declare function collectSavedViews(...sources: unknown[]): Record<string,
|
|
|
91
180
|
* binding exists to remove.
|
|
92
181
|
*/
|
|
93
182
|
export declare function resolveSavedView(views: Record<string, ElementSavedView>, requested: string | undefined | null, object: string): ElementSavedView | undefined;
|
|
183
|
+
/**
|
|
184
|
+
* The diagnostic half of {@link savedViewLimit} and {@link bindingLimit}.
|
|
185
|
+
* `null` means "nothing to say".
|
|
186
|
+
*
|
|
187
|
+
* ## Which operand
|
|
188
|
+
*
|
|
189
|
+
* `operand` picks the refusal to describe; it defaults to `'view'`, the one
|
|
190
|
+
* this builder described before objectui#10016. Each operand gets its own
|
|
191
|
+
* sentence, and each names its operand, so the two refusals stay
|
|
192
|
+
* distinguishable when both fire. They do fire together when the binding AND
|
|
193
|
+
* the view each carry a refused cap: two declarations, one message each.
|
|
194
|
+
*
|
|
195
|
+
* `binding` is the binding as authored. The view's sentence needs it too: it
|
|
196
|
+
* says the consumer falls back to its own default, and that is only true when
|
|
197
|
+
* the binding supplied no usable cap. A usable binding cap is used whatever the
|
|
198
|
+
* view carries, so a refused view cap under one changed nothing and is not
|
|
199
|
+
* reported. That condition lives HERE, once, rather than at each caller,
|
|
200
|
+
* because every caller needs it and two copies could drift.
|
|
201
|
+
*
|
|
202
|
+
* ## Why a BUILDER here, and not a warning from the composer
|
|
203
|
+
*
|
|
204
|
+
* This is the shape {@link elementDataSourceViewNotFoundMessage} already
|
|
205
|
+
* established in this module, for the reason its doc gives: built here so every
|
|
206
|
+
* caller reports the same defect the same way. It is followed rather than
|
|
207
|
+
* re-decided because the alternative is ruled out mechanically —
|
|
208
|
+
* {@link composeElementDataSource} is PURE and the render path calls it from a
|
|
209
|
+
* `useMemo`, so a warning emitted inside it would fire during render and fire
|
|
210
|
+
* again on every re-render. The repaired relay one layer up emits from an
|
|
211
|
+
* effect for exactly that reason; a pure function has no effect to emit from,
|
|
212
|
+
* so it hands the caller the words instead.
|
|
213
|
+
*
|
|
214
|
+
* ## Why the message has to exist at all
|
|
215
|
+
*
|
|
216
|
+
* Dropping alone would be a SILENT change on the renderer path: today the
|
|
217
|
+
* refused value travels as far as a block whose own guard reports it once, and
|
|
218
|
+
* after this repair that block receives nothing and correctly says nothing —
|
|
219
|
+
* an absent key is not a mistake. So the repair would remove the only place the
|
|
220
|
+
* author was being told. The layer that makes the decision is the layer that
|
|
221
|
+
* reports it.
|
|
222
|
+
*
|
|
223
|
+
* ⛔ NOT a second guard: the predicate lives once, in {@link isUsableRowLimit},
|
|
224
|
+
* and the view's carrier is read once, by {@link savedViewRawLimit}; this reads
|
|
225
|
+
* both.
|
|
226
|
+
*
|
|
227
|
+
* ⚠️ It speaks only about a cap THIS layer dropped. A non-numeric
|
|
228
|
+
* `pagination.pageSize` never became a limit here, before or after, so there is
|
|
229
|
+
* nothing for this layer to report about it. A non-numeric binding `limit` is
|
|
230
|
+
* different: `??` used to pass it through, so this layer now drops it and says
|
|
231
|
+
* so.
|
|
232
|
+
*/
|
|
233
|
+
export declare function elementDataSourceRefusedLimitMessage(view: ElementSavedView | null | undefined, viewName: string | undefined | null, object: string, binding?: {
|
|
234
|
+
limit?: unknown;
|
|
235
|
+
} | null, operand?: 'view' | 'binding'): string | null;
|
|
94
236
|
/**
|
|
95
237
|
* Compose an `ElementDataSource` binding with the saved view it names.
|
|
96
238
|
*
|
|
97
239
|
* Pure — the caller resolves the view (see {@link resolveSavedView}) and owns
|
|
98
240
|
* the "named a view that does not exist" decision. Passing `undefined` /`null`
|
|
99
|
-
* for `view` composes a binding with no view, which is exactly the pre
|
|
241
|
+
* for `view` composes a binding with no view, which is exactly the pre-objectstack-ai/objectstack#5576
|
|
100
242
|
* behaviour for the other four keys.
|
|
101
243
|
*
|
|
102
244
|
* See the module doc for the per-key rule table; `filter` is the only key that
|
|
@@ -109,5 +251,117 @@ export declare function composeElementDataSource(config: ElementDataSourceConfig
|
|
|
109
251
|
* have. Built here so the render path and `ViewDataProvider` report the same
|
|
110
252
|
* defect the same way, and so the known-view list (the thing that actually gets
|
|
111
253
|
* an author unstuck) is never omitted by one caller.
|
|
254
|
+
*
|
|
255
|
+
* ## Why the empty case may not say "this object has no saved views"
|
|
256
|
+
*
|
|
257
|
+
* An EMPTY `knownViews` reaches this function from two different worlds, and
|
|
258
|
+
* this function cannot tell them apart:
|
|
259
|
+
*
|
|
260
|
+
* 1. the saved views were read and there are none;
|
|
261
|
+
* 2. the read never successfully happened.
|
|
262
|
+
*
|
|
263
|
+
* World 2 is not hypothetical and not a bug to be fixed upstream: the shipped
|
|
264
|
+
* `ObjectStackAdapter.listViews` contract degrades EVERY failure — refused,
|
|
265
|
+
* offline, malformed — to `[]` on the RESOLVED path (deliberate, and objectui#8151
|
|
266
|
+
* keeps it), so a broken read arrives at `useElementDataSource` as a successful
|
|
267
|
+
* empty answer. The hook's own discrimination catches a REJECTION, which is a
|
|
268
|
+
* shape that contract never produces, so nothing separates the two worlds by the
|
|
269
|
+
* time the count lands here.
|
|
270
|
+
*
|
|
271
|
+
* Framework decision objectstack#13906 decision 1 option A — *a thing that could
|
|
272
|
+
* not be READ is not a thing that is ABSENT* — therefore binds this branch: it
|
|
273
|
+
* may only assert what is true in BOTH worlds. It names the possibilities and
|
|
274
|
+
* commits to neither, because committing is exactly the claim nobody established.
|
|
275
|
+
*
|
|
276
|
+
* ⛔ Do not "restore" the shorter sentence by inferring failure from emptiness in
|
|
277
|
+
* either direction — emptiness is what both worlds produce, so it can never tell
|
|
278
|
+
* them apart. Saying more than this needs the failure FACT plumbed in from the
|
|
279
|
+
* adapter (objectui#8151's surface), not a better guess here.
|
|
112
280
|
*/
|
|
113
281
|
export declare function elementDataSourceViewNotFoundMessage(object: string, view: string, knownViews: readonly string[]): string;
|
|
282
|
+
/**
|
|
283
|
+
* The spec key this module resolves, spelled once.
|
|
284
|
+
*
|
|
285
|
+
* Every consumer that needs the NAME (the registry injection, its gates, the
|
|
286
|
+
* pins) reads it from here rather than repeating the literal, so "which key is
|
|
287
|
+
* this?" has exactly one answer in the tree.
|
|
288
|
+
*/
|
|
289
|
+
export declare const ELEMENT_DATA_SOURCE_KEY = "dataSource";
|
|
290
|
+
/**
|
|
291
|
+
* The authoring-surface declaration of {@link ELEMENT_DATA_SOURCE_KEY} — the one
|
|
292
|
+
* copy, emitted into every registration that wraps the runtime gate.
|
|
293
|
+
*
|
|
294
|
+
* ## Why it lives here and is injected rather than written per block
|
|
295
|
+
*
|
|
296
|
+
* `PageComponentSchema.dataSource` is READ by `ElementDataSourceGate` in
|
|
297
|
+
* `@object-ui/react` on behalf of every object-bound block that wraps itself in
|
|
298
|
+
* it — nine of them at the time of writing. It was declared by NONE: `validateTree`
|
|
299
|
+
* skips `BASE_PROPS` and otherwise looks a prop up in the component's `inputs`,
|
|
300
|
+
* and `dataSource` was in neither. So the html tier reported the one spelling
|
|
301
|
+
* that resolves a saved view — the only one that works — with the identical
|
|
302
|
+
* `unknown-prop` warning it gives the spellings that do nothing, on the tier
|
|
303
|
+
* meant to accept AI-authored pages, where the diagnostic IS the contract
|
|
304
|
+
* (objectui#6678, the reporter of objectui#6598 gave up on exactly this).
|
|
305
|
+
*
|
|
306
|
+
* The maintainer ruling of 2026-08-29 took option B **in the injection form**:
|
|
307
|
+
* emitted mechanically at the wrapping seam, so a block declares the key from
|
|
308
|
+
* the same place that reads it. Nine hand-kept copies is the shape that ruling
|
|
309
|
+
* refused — they drift, and a tenth block would simply forget. Adding the key to
|
|
310
|
+
* `sdui-parser`'s `BASE_PROPS` (option A) was refused too: that set mirrors
|
|
311
|
+
* `BaseSchema`, and silencing `dataSource` on blocks that do NOT read it
|
|
312
|
+
* (`flex`, `card`) would make the diagnostic lie in the other direction.
|
|
313
|
+
*
|
|
314
|
+
* ## Why `object`, and not a bare `'object'` kind
|
|
315
|
+
*
|
|
316
|
+
* The `binding: 'object'` marker is what tells a consumer this input names an
|
|
317
|
+
* OBJECT — `validateTree` records it as a binding site, and the designer offers
|
|
318
|
+
* an object picker rather than a free-text blob. The `type` stays the coarse
|
|
319
|
+
* `'object'` kind because the value is a record; the two words are unrelated and
|
|
320
|
+
* both are correct here.
|
|
321
|
+
*
|
|
322
|
+
* ## Typed as the framework's injected input (`9e37d9b39`)
|
|
323
|
+
*
|
|
324
|
+
* `binding` is not a `ComponentInput` member. The maintainer ruling of
|
|
325
|
+
* 2026-09-07 answered the card's product question — may an ordinary
|
|
326
|
+
* registration declare a binding input? — with no, so the ONE writer of the
|
|
327
|
+
* key is this constant, typed by `InjectedComponentInput` from
|
|
328
|
+
* `@object-ui/types`: a `ComponentInput` plus the marker. The hand-written
|
|
329
|
+
* inline type literal that used to sit here was the tell that the key had no
|
|
330
|
+
* authored home, and the splice in `Registry.register` reached the `inputs`
|
|
331
|
+
* array through an `as ComponentMeta` cast. Both are gone: the shape is now
|
|
332
|
+
* checked here and at the splice, and a registration that writes `binding`
|
|
333
|
+
* itself is an excess-property `tsc` error.
|
|
334
|
+
*/
|
|
335
|
+
export declare const ELEMENT_DATA_SOURCE_INPUT: InjectedComponentInput;
|
|
336
|
+
/**
|
|
337
|
+
* Declare that `renderer` wraps `ElementDataSourceGate`, so its registrations
|
|
338
|
+
* publish {@link ELEMENT_DATA_SOURCE_INPUT}.
|
|
339
|
+
*
|
|
340
|
+
* This is the SEAM, and it is deliberately the only way in: `Registry.register`
|
|
341
|
+
* injects the declaration for exactly the renderers marked here, so the key is
|
|
342
|
+
* published by whatever consumes the gate and by nothing else — which is what
|
|
343
|
+
* keeps the `flex` / `card` direction of the ruling true.
|
|
344
|
+
* `scripts/check-element-data-source-declaration.mjs` fails any source that
|
|
345
|
+
* consumes the gate without passing through it, so a new block cannot forget.
|
|
346
|
+
*
|
|
347
|
+
* ## Two import paths, ONE name and ONE function
|
|
348
|
+
*
|
|
349
|
+
* `@object-ui/react` re-exports this beside `ElementDataSourceGate` so the seam is
|
|
350
|
+
* findable next to the thing it is about. That export is for DISCOVERABILITY:
|
|
351
|
+
* every CALL SITE imports from here, and
|
|
352
|
+
* `scripts/check-element-data-source-declaration.mjs` enforces it.
|
|
353
|
+
*
|
|
354
|
+
* The rule is measured, not stylistic. A registration runs at MODULE SCOPE, and
|
|
355
|
+
* `element:record_picker`'s registration in `@object-ui/components` is the
|
|
356
|
+
* worked example. 101 suites in this repo partially
|
|
357
|
+
* mock `@object-ui/react` by hand-listing the exports they return, and a
|
|
358
|
+
* module-scope read of a name those lists do not carry throws at COLLECTION
|
|
359
|
+
* time — the importing test file fails before it runs a single assertion.
|
|
360
|
+
* Measured: 17 files, all four CI shards, zero failed assertions among them.
|
|
361
|
+
* Nothing mocks `@object-ui/core`, and `@object-ui/components` already imports
|
|
362
|
+
* `ComponentRegistry` from it at module scope, so this path adds no coupling
|
|
363
|
+
* that was not already there.
|
|
364
|
+
*/
|
|
365
|
+
export declare function elementDataSourceBlock<C>(renderer: C): C;
|
|
366
|
+
/** Whether `renderer` was marked by {@link elementDataSourceBlock}. */
|
|
367
|
+
export declare function isElementDataSourceBlock(renderer: unknown): boolean;
|