@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
|
@@ -32,7 +32,10 @@
|
|
|
32
32
|
* predicate.)
|
|
33
33
|
*
|
|
34
34
|
* Since objectstack#8018 the same reader also carries the non-predicate row keys
|
|
35
|
-
* a view's actions read
|
|
35
|
+
* a view's actions read: `recordIdField`, since objectui#10277 the field a
|
|
36
|
+
* `defaultFromRow` param seeds from and the `{field}` tokens of an action's
|
|
37
|
+
* `target`, and since objectui#10404 the fields an `undoable` action writes,
|
|
38
|
+
* whose prior values the Undo capture reads off the row. The name kept the
|
|
36
39
|
* "predicate" spelling because the mechanism is identical (harvest a name, gate
|
|
37
40
|
* it against the declared fields, add it to `$select`); see
|
|
38
41
|
* {@link listViewPredicates} for what is in the set and why.
|
|
@@ -88,6 +91,77 @@ const RECORD_REF = /\b(?:record|data)\.([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
|
88
91
|
* declaration contributes nothing instead of contributing a plausible wrong name.
|
|
89
92
|
*/
|
|
90
93
|
const BARE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
94
|
+
/**
|
|
95
|
+
* A `{field}` token in an action's `target` — the grammar the console's `api`
|
|
96
|
+
* handler (`useConsoleActionRuntime` in `@object-ui/app-shell`) substitutes from
|
|
97
|
+
* the row record, spelled exactly as that handler spells it, so the harvest
|
|
98
|
+
* reads the same tokens it fills. The capture is a bare identifier by
|
|
99
|
+
* construction: a dotted path (`{owner.name}`), an expression (`{a + b}`) and
|
|
100
|
+
* the runner's own `${param.X}` / `${ctx.X}` scopes all fall outside it and
|
|
101
|
+
* match nothing here — this grammar does not read them off the row, so they owe
|
|
102
|
+
* the projection nothing.
|
|
103
|
+
*/
|
|
104
|
+
const TARGET_ROW_TOKEN = /\{([a-z_][a-z0-9_]*)\}/gi;
|
|
105
|
+
/**
|
|
106
|
+
* The row key a `defaultFromRow` param seeds its value from — `field` wins,
|
|
107
|
+
* because row data is keyed by object field; `name` is the fallback. The same
|
|
108
|
+
* precedence `resolveActionParam` (`@object-ui/fields`) reads, which is the
|
|
109
|
+
* point: the projection must carry the key the runtime looks up, not the
|
|
110
|
+
* param's payload name. `remove_team_member` seeds `teamId` from `team_id`.
|
|
111
|
+
*/
|
|
112
|
+
function defaultFromRowKey(param) {
|
|
113
|
+
if (!param || typeof param !== 'object')
|
|
114
|
+
return undefined;
|
|
115
|
+
const p = param;
|
|
116
|
+
if (!p.defaultFromRow)
|
|
117
|
+
return undefined;
|
|
118
|
+
return p.field ?? p.name;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The keys an `undoable` action WRITES, which are the keys its Undo capture
|
|
122
|
+
* reads off the row (objectui#10404). Returned unfiltered; the caller applies
|
|
123
|
+
* the identifier gate.
|
|
124
|
+
*
|
|
125
|
+
* Each source is the one a writer actually merges into the written set:
|
|
126
|
+
*
|
|
127
|
+
* - `patch`: the static field values of `operation: 'update'`, merged under the
|
|
128
|
+
* collected params (`ActionRunner`'s `executeUpdateOperation`). Every def
|
|
129
|
+
* this harvest reads is a definition its writer applies whole (a view's
|
|
130
|
+
* resolved row def, a `bulkActionDefs` entry, an object's action), never a
|
|
131
|
+
* page action row, so this is the declared `patch` the write applies: the
|
|
132
|
+
* one the runner's Undo snapshot reads too (objectui#13022);
|
|
133
|
+
* - each param's COLLECTED key, `name ?? field`: the key the param dialog
|
|
134
|
+
* stores its answer under (`paramName` in `app-shell`'s
|
|
135
|
+
* `resolveActionParams`). This is the opposite precedence to
|
|
136
|
+
* {@link defaultFromRowKey} on purpose: that one asks which row key a value
|
|
137
|
+
* is READ from, this one asks which key a value is WRITTEN under, and the
|
|
138
|
+
* capture looks the written key up on the row;
|
|
139
|
+
* - `bodyExtra`: the static body the console's generic `api` handler merges
|
|
140
|
+
* over the params before it writes (`useConsoleActionRuntime`).
|
|
141
|
+
*
|
|
142
|
+
* `recordId` is left out: both writers read it as the record's address and
|
|
143
|
+
* strip it from the written set, so no capture ever looks it up.
|
|
144
|
+
*
|
|
145
|
+
* A key the harvest cannot make the row carry (undeclared, or a field the
|
|
146
|
+
* principal may not read) is not rescued here. The capture refuses to invent a
|
|
147
|
+
* prior value for it instead, and offers no Undo.
|
|
148
|
+
*/
|
|
149
|
+
function undoableWrittenKeys(def) {
|
|
150
|
+
const keys = [];
|
|
151
|
+
for (const bag of [def.patch, def.bodyExtra]) {
|
|
152
|
+
if (bag && typeof bag === 'object' && !Array.isArray(bag))
|
|
153
|
+
keys.push(...Object.keys(bag));
|
|
154
|
+
}
|
|
155
|
+
if (Array.isArray(def.params)) {
|
|
156
|
+
for (const param of def.params) {
|
|
157
|
+
if (!param || typeof param !== 'object')
|
|
158
|
+
continue;
|
|
159
|
+
const p = param;
|
|
160
|
+
keys.push(p.name ?? p.field);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return keys.filter((key) => key !== 'recordId');
|
|
164
|
+
}
|
|
91
165
|
/** Pull the CEL source out of any of the shapes a predicate is authored in. */
|
|
92
166
|
function predicateSource(pred) {
|
|
93
167
|
if (typeof pred === 'string')
|
|
@@ -139,24 +213,54 @@ export function collectPredicateFieldRefs(predicates) {
|
|
|
139
213
|
* kebab (custom defs AND the built-in Edit/Delete overrides), the selection bar,
|
|
140
214
|
* and the object's declared actions.
|
|
141
215
|
*
|
|
142
|
-
* Most entries are predicates.
|
|
216
|
+
* Most entries are predicates. Five are not, and all are spelled as a synthetic
|
|
143
217
|
* `record.<name>` so the one harvester handles them: conditional formatting's
|
|
144
|
-
* native `{ field, operator, value }` shape,
|
|
145
|
-
* (objectstack#8018)
|
|
146
|
-
*
|
|
218
|
+
* native `{ field, operator, value }` shape, an action's `recordIdField`
|
|
219
|
+
* (objectstack#8018), the field each `defaultFromRow` param seeds from, each
|
|
220
|
+
* `{field}` token of the action's `target` (both objectui#10277), and the fields
|
|
221
|
+
* an `undoable` action writes (objectui#10404). The
|
|
222
|
+
* `recordIdField` case is the same *class* of bug the predicate harvest exists
|
|
223
|
+
* to close, one surface over — the action runtime reads
|
|
147
224
|
* `rowRecord[action.recordIdField]` to seed `recordIdParam`, so a key outside the
|
|
148
225
|
* listView columns arrived absent and the injection was silently skipped, which
|
|
149
226
|
* turns a record-scoped mutation into one that names no record while still
|
|
150
227
|
* reporting success. The default (`id`) is already projected unconditionally, so
|
|
151
228
|
* only an explicit declaration adds anything here.
|
|
152
229
|
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* and
|
|
230
|
+
* The last two are the same class again, and they were silent in the same way.
|
|
231
|
+
* `resolveActionParam` seeds a `defaultFromRow` param only when the row HAS the
|
|
232
|
+
* key (an own-property check), so an absent key opened the param dialog with the
|
|
233
|
+
* field blank; and the console's `api` handler fills a `{field}` token from the
|
|
234
|
+
* row, so an absent key put an empty path segment into the URL. The key a param
|
|
235
|
+
* seeds from is `field ?? name` — the runtime's precedence, not the param's
|
|
236
|
+
* payload name — and the token grammar is the handler's own. Neither is
|
|
237
|
+
* narrowed by the action's `type`, for the asymmetry stated below: an extra
|
|
238
|
+
* column costs bytes.
|
|
239
|
+
*
|
|
240
|
+
* The written fields are the same class once more, and the worst of them,
|
|
241
|
+
* because what went wrong was a write. The Undo capture of an `undoable` update
|
|
242
|
+
* reads each written field's prior value off the row; on a projected row a
|
|
243
|
+
* written field no column shows was absent, the capture recorded `null`, and
|
|
244
|
+
* Undo then wrote `null` over the stored value it existed to restore. The
|
|
245
|
+
* written keys are `patch`, each param's collected key and `bodyExtra` (see
|
|
246
|
+
* `undoableWrittenKeys`). This one IS narrowed, to `undoable`, and not for
|
|
247
|
+
* cost: the capture is the only reader of a written field's prior value, and it
|
|
248
|
+
* runs only under `undoable`, so without it the row reads none of these keys.
|
|
249
|
+
*
|
|
250
|
+
* A `recordIdField`, param key or written key that is not a bare identifier is
|
|
251
|
+
* dropped here
|
|
252
|
+
* (a `target` token is one by construction of its grammar), and a name the
|
|
253
|
+
* object does not declare is dropped by the caller's `isProjectableField`
|
|
254
|
+
* guard. Both drops are the safe direction: such a name is not a column
|
|
255
|
+
* anywhere, and an unknown key in `$select` is not ignored by every backend.
|
|
256
|
+
* The identifier gate is not decoration — without it the harvester's regex would
|
|
257
|
+
* read a PREFIX of a malformed name (`record.not a field` → `not`) and
|
|
258
|
+
* contribute a plausible wrong field instead of nothing.
|
|
259
|
+
*
|
|
260
|
+
* ⛔ Harvesting is not a read grant. Every name here is a CANDIDATE: each
|
|
261
|
+
* consumer still passes it through its own field-level read filter before it
|
|
262
|
+
* reaches `$select`, so a field the principal may not read is not requested
|
|
263
|
+
* because an action names it.
|
|
160
264
|
*
|
|
161
265
|
* `objectActions` is deliberately the object's WHOLE action set rather than
|
|
162
266
|
* only the ones this view promotes. Narrowing it would mean re-running the
|
|
@@ -191,6 +295,31 @@ export function listViewPredicates(view) {
|
|
|
191
295
|
if (typeof d.recordIdField === 'string' && BARE_IDENTIFIER.test(d.recordIdField)) {
|
|
192
296
|
preds.push(`record.${d.recordIdField}`);
|
|
193
297
|
}
|
|
298
|
+
// A `defaultFromRow` param's seed field and the `{field}` tokens of the
|
|
299
|
+
// action's `target` (objectui#10277) — two more row keys the runtime
|
|
300
|
+
// READS, by the same rule and the same identifier gate.
|
|
301
|
+
if (Array.isArray(d.params)) {
|
|
302
|
+
for (const param of d.params) {
|
|
303
|
+
const key = defaultFromRowKey(param);
|
|
304
|
+
if (typeof key === 'string' && BARE_IDENTIFIER.test(key))
|
|
305
|
+
preds.push(`record.${key}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (typeof d.target === 'string') {
|
|
309
|
+
TARGET_ROW_TOKEN.lastIndex = 0;
|
|
310
|
+
for (let m = TARGET_ROW_TOKEN.exec(d.target); m; m = TARGET_ROW_TOKEN.exec(d.target)) {
|
|
311
|
+
preds.push(`record.${m[1]}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
// The fields an `undoable` action writes (objectui#10404): its Undo
|
|
315
|
+
// capture reads each one's prior value off the row. Same rule, same
|
|
316
|
+
// identifier gate.
|
|
317
|
+
if (d.undoable) {
|
|
318
|
+
for (const key of undoableWrittenKeys(d)) {
|
|
319
|
+
if (typeof key === 'string' && BARE_IDENTIFIER.test(key))
|
|
320
|
+
preds.push(`record.${key}`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
194
323
|
}
|
|
195
324
|
}
|
|
196
325
|
for (const override of Object.values(view.userActions ?? {})) {
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
* Resolve the colour a record's value stands for, off the field's own metadata.
|
|
10
|
+
*
|
|
11
|
+
* ## Why this is shared (objectui#7243)
|
|
12
|
+
*
|
|
13
|
+
* `colorField` is one authored key — `gantt.colorField`, `calendar.colorField`,
|
|
14
|
+
* `timeline.colorField` — and it used to mean three different things:
|
|
15
|
+
*
|
|
16
|
+
* - `plugin-timeline` resolved the field's option `color` (correct);
|
|
17
|
+
* - `plugin-calendar` passed the raw value on to a deterministic hash, so the
|
|
18
|
+
* colour the author declared on the option was never consulted;
|
|
19
|
+
* - `plugin-gantt` passed the raw value straight into `backgroundColor`, so
|
|
20
|
+
* `colorField: 'status'` emitted `backgroundColor: "open"` — not a colour.
|
|
21
|
+
* The browser dropped the declaration and every bar rendered identically,
|
|
22
|
+
* which made DECLARING the documented key strictly worse than omitting it.
|
|
23
|
+
*
|
|
24
|
+
* The timeline's resolver is lifted here verbatim so all three answer the same
|
|
25
|
+
* question the same way. What lives here is the part that is genuinely common —
|
|
26
|
+
* the two rungs that depend only on the field metadata and the stored value:
|
|
27
|
+
*
|
|
28
|
+
* 1. the field's own option `color` for that value;
|
|
29
|
+
* 2. the value itself when it already IS a CSS colour literal.
|
|
30
|
+
*
|
|
31
|
+
* The LAST rung deliberately stays with each caller, because each one has a
|
|
32
|
+
* different right answer for "a value that is neither": the gantt derives a
|
|
33
|
+
* semantic token hex (a bar must be painted), the calendar hashes onto its
|
|
34
|
+
* theme-aware 8-stop class palette (a soft tint, not a solid fill), and the
|
|
35
|
+
* timeline draws its default marker variant. Pulling those together would be a
|
|
36
|
+
* repaint of existing views, not a bug fix.
|
|
37
|
+
*
|
|
38
|
+
* ## Keyed by option `value` only
|
|
39
|
+
*
|
|
40
|
+
* Deliberately NOT `buildOptionColorMap` (`./chart-series.ts`), which keys by
|
|
41
|
+
* value AND display label: that one reads dataset ROWS, where the server may
|
|
42
|
+
* have resolved a select dimension to its label. These callers read RECORDS,
|
|
43
|
+
* where `record[colorField]` is the stored value. Keying by label as well would
|
|
44
|
+
* colour a record whose stored value happens to collide with another option's
|
|
45
|
+
* label — a tolerance nothing here needs, and one the timeline never had.
|
|
46
|
+
*/
|
|
47
|
+
/** One entry of a select-like field's `options`, as far as colour is concerned. */
|
|
48
|
+
export interface FieldColorOption {
|
|
49
|
+
value?: unknown;
|
|
50
|
+
color?: unknown;
|
|
51
|
+
}
|
|
52
|
+
/** The slice of a field definition this resolver reads. */
|
|
53
|
+
export interface FieldColorDefinition {
|
|
54
|
+
options?: unknown;
|
|
55
|
+
}
|
|
56
|
+
/** Does this string already name a colour the DOM can paint? */
|
|
57
|
+
export declare function isColorLiteral(value: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Build a `value -> option colour` lookup from a field definition's `options`.
|
|
60
|
+
* Returns an empty map for a field with no options, so callers need no guard.
|
|
61
|
+
*/
|
|
62
|
+
export declare function buildFieldColorMap(fieldDef: FieldColorDefinition | undefined | null): Record<string, string>;
|
|
63
|
+
/**
|
|
64
|
+
* Build the per-field colour resolver: rung 1 (option colour) then rung 2 (the
|
|
65
|
+
* value is already a colour literal), else `undefined` so the caller runs its
|
|
66
|
+
* own last rung.
|
|
67
|
+
*
|
|
68
|
+
* A factory rather than a plain function because callers resolve one field
|
|
69
|
+
* across every row of a view — the option map is built once, not per record.
|
|
70
|
+
*
|
|
71
|
+
* @param fieldDef `objectSchema.fields[colorField]`, or `undefined` when the
|
|
72
|
+
* view has no schema in hand (an inline-data or `api`-provider view). With no
|
|
73
|
+
* options, rung 1 is a no-op and rung 2 still answers for literal colours.
|
|
74
|
+
*/
|
|
75
|
+
export declare function createFieldColorResolver(fieldDef: FieldColorDefinition | undefined | null): (value: unknown) => string | undefined;
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
* Colour literals the renderers accept as-is.
|
|
10
|
+
*
|
|
11
|
+
* Hex accepts 3, 6 and 8 digits. The timeline's private regex accepted 3 and 6;
|
|
12
|
+
* `plugin-calendar`'s accepted 3, 6 and 8 — two in-repo spellings of the same
|
|
13
|
+
* question, and the narrow one is the only one under which a VALID CSS colour
|
|
14
|
+
* (`#rrggbbaa`) could fall past this rung and be replaced by a derived colour.
|
|
15
|
+
* Widening can only turn "not recognised" into "the author's colour"; it can
|
|
16
|
+
* never turn a colour into something else. So the wider spelling wins.
|
|
17
|
+
*/
|
|
18
|
+
const HEX_COLOR_RE = /^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
19
|
+
/** Does this string already name a colour the DOM can paint? */
|
|
20
|
+
export function isColorLiteral(value) {
|
|
21
|
+
return HEX_COLOR_RE.test(value) || value.startsWith('rgb') || value.startsWith('hsl');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build a `value -> option colour` lookup from a field definition's `options`.
|
|
25
|
+
* Returns an empty map for a field with no options, so callers need no guard.
|
|
26
|
+
*/
|
|
27
|
+
export function buildFieldColorMap(fieldDef) {
|
|
28
|
+
const options = fieldDef?.options;
|
|
29
|
+
if (!Array.isArray(options))
|
|
30
|
+
return {};
|
|
31
|
+
const map = {};
|
|
32
|
+
for (const opt of options) {
|
|
33
|
+
if (!opt || typeof opt !== 'object')
|
|
34
|
+
continue;
|
|
35
|
+
if (opt.value == null)
|
|
36
|
+
continue;
|
|
37
|
+
if (typeof opt.color === 'string' && opt.color)
|
|
38
|
+
map[String(opt.value)] = opt.color;
|
|
39
|
+
}
|
|
40
|
+
return map;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build the per-field colour resolver: rung 1 (option colour) then rung 2 (the
|
|
44
|
+
* value is already a colour literal), else `undefined` so the caller runs its
|
|
45
|
+
* own last rung.
|
|
46
|
+
*
|
|
47
|
+
* A factory rather than a plain function because callers resolve one field
|
|
48
|
+
* across every row of a view — the option map is built once, not per record.
|
|
49
|
+
*
|
|
50
|
+
* @param fieldDef `objectSchema.fields[colorField]`, or `undefined` when the
|
|
51
|
+
* view has no schema in hand (an inline-data or `api`-provider view). With no
|
|
52
|
+
* options, rung 1 is a no-op and rung 2 still answers for literal colours.
|
|
53
|
+
*/
|
|
54
|
+
export function createFieldColorResolver(fieldDef) {
|
|
55
|
+
const colorByValue = buildFieldColorMap(fieldDef);
|
|
56
|
+
return (value) => {
|
|
57
|
+
if (value == null || value === '')
|
|
58
|
+
return undefined;
|
|
59
|
+
const key = String(value);
|
|
60
|
+
const optionColor = colorByValue[key];
|
|
61
|
+
if (optionColor)
|
|
62
|
+
return optionColor;
|
|
63
|
+
return isColorLiteral(key) ? key : undefined;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI — the shared record-source readers: the ruled three-rung ladder
|
|
3
|
+
* (`resolveRecordSourceConfig`) and the object-name it resolves to
|
|
4
|
+
* (`resolveRecordSourceObjectName`)
|
|
5
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
6
|
+
*
|
|
7
|
+
* This source code is licensed under the MIT license found in the
|
|
8
|
+
* LICENSE file in the root directory of this source tree.
|
|
9
|
+
*/
|
|
10
|
+
import type { ViewData } from '@object-ui/types';
|
|
11
|
+
/**
|
|
12
|
+
* The object a view block is bound to, resolved ONCE for the whole renderer
|
|
13
|
+
* (`b041b9c0c`).
|
|
14
|
+
*
|
|
15
|
+
* ## The question this answers, and the one it does not
|
|
16
|
+
*
|
|
17
|
+
* There are TWO separately-ruled precedence questions about `objectName`, and
|
|
18
|
+
* they only look like one question when a single reader is asked to answer
|
|
19
|
+
* both:
|
|
20
|
+
*
|
|
21
|
+
* 1. **Which object does this block RESOLVE, at render time, when it carries
|
|
22
|
+
* more than one binding?** — the published three-rung record-source ladder
|
|
23
|
+
* (`data`, then `staticData`, then `objectName`), declared on both faces of
|
|
24
|
+
* the contract (`ObjectMapSchema.objectName` / `ObjectGanttSchema.objectName`
|
|
25
|
+
* in `@object-ui/types`, and the `.describe` on their zod twins — the map
|
|
26
|
+
* twin's reads *"objectName — the THIRD record source `getDataConfig`
|
|
27
|
+
* resolves, after `data` and `staticData`"*, naming `ObjectMap.tsx`'s local
|
|
28
|
+
* wrapper; the gantt twin names `resolveRecordSourceConfig` itself), ruled
|
|
29
|
+
* 2026-09-02 (landed as `77cb489b4`) and pinned by
|
|
30
|
+
* `objectql-record-source-refinement-6939.test.ts`. **That is this
|
|
31
|
+
* function.**
|
|
32
|
+
* 2. **How does `objectName` get POPULATED when it is absent?** — the
|
|
33
|
+
* authoring-time gap-fill in `normalizeListViewSchema` (objectui#7477,
|
|
34
|
+
* ruling B, landed as `00d2fa682`), where an `objectName` already on the schema WINS
|
|
35
|
+
* and the `data` block only fills a gap: *"it can never re-point a binding
|
|
36
|
+
* that already resolves."*
|
|
37
|
+
*
|
|
38
|
+
* The two are NOT merged and neither is re-pointed at the other. Merging them
|
|
39
|
+
* would override a standing maintainer ruling in whichever direction the merged
|
|
40
|
+
* reader happened to pick: at the sites below the binding that already resolves
|
|
41
|
+
* IS `data.object`, so ruling B's own words argue for keeping rung 1 as it is.
|
|
42
|
+
*
|
|
43
|
+
* ## Why `staticData` does not appear here
|
|
44
|
+
*
|
|
45
|
+
* The ladder's second rung wraps inline rows as `{ provider: 'value', items }`,
|
|
46
|
+
* which names no object at all. So for the object-NAME question the three-rung
|
|
47
|
+
* ladder reduces to two rungs — the resolved config's object when it names one,
|
|
48
|
+
* else the schema's own `objectName`, which is what a `value`-backed block
|
|
49
|
+
* still needs for metadata reads, i18n field labels and permission verdicts.
|
|
50
|
+
* Callers pass the ALREADY-RESOLVED config (their
|
|
51
|
+
* {@link resolveRecordSourceConfig} output, direct or through a local wrapper),
|
|
52
|
+
* so rung ordering is settled before this function is reached.
|
|
53
|
+
*
|
|
54
|
+
* ## No lenient rung was added (AGENTS.md #0.1)
|
|
55
|
+
*
|
|
56
|
+
* `ViewDataSchema`'s `object` provider is a `strictObject` carrying exactly
|
|
57
|
+
* `{ provider, object }` with `object` REQUIRED, so `{ provider: 'object' }`
|
|
58
|
+
* without an `object` is off-contract. This reader does not coerce that shape
|
|
59
|
+
* back to `objectName`; the two call sites that used to (`ObjectGrid`'s
|
|
60
|
+
* `'object' in dataConfig` test and `ObjectTree`'s header `?? schema.objectName`
|
|
61
|
+
* tail) keep their own tail at the site, so the collapse changes nothing they
|
|
62
|
+
* resolve today while the shared rung stays contract-strict.
|
|
63
|
+
*
|
|
64
|
+
* @param schema - The block's schema; only `objectName` is read.
|
|
65
|
+
* @param dataConfig - The RESOLVED data config — the caller's own
|
|
66
|
+
* {@link resolveRecordSourceConfig} output, `null` when nothing is bound.
|
|
67
|
+
* @returns The bound object's name, or `undefined` when neither the resolved
|
|
68
|
+
* config nor the schema names one.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* const dataConfig = useMemo(() => resolveRecordSourceConfig(schema, 'view-data'), [schema]);
|
|
73
|
+
* const objectName = resolveRecordSourceObjectName(schema, dataConfig);
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare function resolveRecordSourceObjectName(schema: {
|
|
77
|
+
objectName?: string;
|
|
78
|
+
} | null | undefined, dataConfig: {
|
|
79
|
+
provider?: string;
|
|
80
|
+
object?: string;
|
|
81
|
+
} | null | undefined): string | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* Which arm of `data` a block's PUBLISHED row declares — the only shape rung 1
|
|
84
|
+
* of the ladder honours for that block (objectui#8348).
|
|
85
|
+
*
|
|
86
|
+
* Maintainer ruling, decision batch #83 (2026-09-08), verbatim: 「8348 以协议为准」
|
|
87
|
+
* — the contract decides. A renderer honours the `data` spelling its block's
|
|
88
|
+
* published row declares and no other: what `os validate` and the save gate
|
|
89
|
+
* refuse, the renderer refuses too.
|
|
90
|
+
*
|
|
91
|
+
* - `'view-data'` — the row is the spec's `ViewData` discriminated union, four
|
|
92
|
+
* strict OBJECT arms on `provider`. A bare array under `data` is NOT a record
|
|
93
|
+
* source for such a block.
|
|
94
|
+
* - `'array'` — the row is `z.array(...)`: an array of PRE-FETCHED RECORDS. The
|
|
95
|
+
* `{ provider, items }` config object is NOT a record source for such a
|
|
96
|
+
* block.
|
|
97
|
+
* - `'undeclared'` — no published face declares a `data` row for the block at
|
|
98
|
+
* all, so neither arm of the ruling reaches it and rung 1 keeps its pre-8348
|
|
99
|
+
* verbatim behaviour. ⛔ NOT a tolerance to copy: it is the honest answer for
|
|
100
|
+
* a block the ruling does not decide, and it is reported rather than guessed.
|
|
101
|
+
* No ladder block passes it any more: `object-tree`, the last one that did,
|
|
102
|
+
* gained a published row and moved to `'view-data'` (objectui#8348, ruling
|
|
103
|
+
* batch #136 item 3, Q1-C). It stays the answer
|
|
104
|
+
* {@link recordSourceDataArmForType} gives a type it does not list.
|
|
105
|
+
*
|
|
106
|
+
* The arm is passed BY THE CALL SITE rather than looked up from `schema.type`
|
|
107
|
+
* on purpose. Most of these renderers are registered twice — `object-grid`
|
|
108
|
+
* and the `view:grid` alias `grid`, `object-calendar` and `calendar`, and so on
|
|
109
|
+
* (the bare `gantt` and `map` keys are retired, objectui#8008 / objectui#10393)
|
|
110
|
+
* — so a node reaches the same component under either spelling, and a table
|
|
111
|
+
* keyed by `type` would answer for one tag and silently miss the other. A
|
|
112
|
+
* REQUIRED parameter makes the arm a compile-time obligation at each of the
|
|
113
|
+
* five sites instead.
|
|
114
|
+
*/
|
|
115
|
+
export type RecordSourceDataArm = 'view-data' | 'array' | 'undeclared';
|
|
116
|
+
/**
|
|
117
|
+
* The authored `data` shape a block ON THAT ARM may declare — the DECLARATION
|
|
118
|
+
* half of the same per-block ruling {@link authoredDataIsOnTheDeclaredArm}
|
|
119
|
+
* decides at RUNTIME (`ab856ed30`).
|
|
120
|
+
*
|
|
121
|
+
* ## The contradiction this closes
|
|
122
|
+
*
|
|
123
|
+
* {@link resolveRecordSourceConfig} used to declare its `data` parameter as a
|
|
124
|
+
* flat `ViewData`, while its own docblock, its REQUIRED `dataArm` parameter and
|
|
125
|
+
* its rung-1 predicate all say the honoured shape DIFFERS PER BLOCK — and
|
|
126
|
+
* `ViewData` is a discriminated union over four strict OBJECT arms with no
|
|
127
|
+
* array member. So the signature refused the one block whose published row IS
|
|
128
|
+
* the array arm, and the arm parameter that exists to make the row a
|
|
129
|
+
* compile-time obligation was the one thing the compiler ignored. The
|
|
130
|
+
* contradiction was held shut at `ObjectCalendar.tsx`'s call site by an
|
|
131
|
+
* `as ViewData | undefined` cast whose own comment reported it as an upstream
|
|
132
|
+
* defect rather than a licence.
|
|
133
|
+
*
|
|
134
|
+
* ⛔ The repair is NOT a union of the arms. `ViewData | unknown[]` would let a
|
|
135
|
+
* `'view-data'` site declare a bare array and a `'array'` site declare a
|
|
136
|
+
* provider block — each of them the shape that block's row REFUSES, each of
|
|
137
|
+
* them compiling — which is the repo-wide default {@link RecordSourceDataArm}'s
|
|
138
|
+
* docblock refuses by name: *"a default is how the second de-facto contract got
|
|
139
|
+
* in"* (AGENTS.md #0.1).
|
|
140
|
+
*
|
|
141
|
+
* - `'view-data'` — the spec's `ViewData`. UNCHANGED, and that matters: this
|
|
142
|
+
* is exactly what the flat declaration said, and it was right for the three
|
|
143
|
+
* blocks on this arm, so nothing moves for `object-grid`, `object-map` or
|
|
144
|
+
* `object-gantt`.
|
|
145
|
+
* - `'array'` — `unknown[]`, the shape `object-calendar`'s published row
|
|
146
|
+
* declares (`ComponentPropsMap['object-calendar'].data` is
|
|
147
|
+
* `z.array(z.unknown()).optional()`, *"Pre-fetched records"*).
|
|
148
|
+
* - `'undeclared'` — `unknown`. No published face declares a `data` row for
|
|
149
|
+
* such a block, so rung 1 keeps its pre-8348 verbatim behaviour and honours
|
|
150
|
+
* ANY truthy value. Anything narrower here would be a rule nobody has ruled.
|
|
151
|
+
*
|
|
152
|
+
* ⚠️ RUNTIME IS UNMOVED. This type is erased; {@link authoredDataIsOnTheDeclaredArm}
|
|
153
|
+
* still takes `unknown` and still decides rung 1 on the value's KIND, so every
|
|
154
|
+
* runtime verdict — including the off-arm ones — is byte-for-byte what it was.
|
|
155
|
+
* `record-source-config.behaviourNeutrality-7632.test.ts` passes its fixtures
|
|
156
|
+
* through `as any` precisely so it keeps measuring that, whatever the
|
|
157
|
+
* declaration says.
|
|
158
|
+
*
|
|
159
|
+
* ⚠️ DISTRIBUTIVE on purpose, and that is why this is a conditional type rather
|
|
160
|
+
* than a set of overloads. A caller holding a `RecordSourceDataArm`-typed
|
|
161
|
+
* VARIABLE instead of a literal gets `ViewData | unknown[] | unknown`, i.e.
|
|
162
|
+
* `unknown` — the honest answer when the arm is not decided at compile time.
|
|
163
|
+
* The same caller would match NONE of three literal-armed overloads, which
|
|
164
|
+
* would turn a working call into an error for no contract reason.
|
|
165
|
+
*/
|
|
166
|
+
export type AuthoredRecordSourceData<Arm extends RecordSourceDataArm> = Arm extends 'view-data' ? ViewData : Arm extends 'array' ? unknown[] : unknown;
|
|
167
|
+
/**
|
|
168
|
+
* The block's record source, resolved from the ruled three-rung ladder
|
|
169
|
+
* (`ce2aaefe1`), with rung 1 judged against the block's own published `data`
|
|
170
|
+
* row (objectui#8348).
|
|
171
|
+
*
|
|
172
|
+
* ## The ruled contract this is the ONE implementation of
|
|
173
|
+
*
|
|
174
|
+
* `data`, then `staticData`, then `objectName` — declared on both faces of the
|
|
175
|
+
* published contract and pinned by
|
|
176
|
+
* `objectql-record-source-refinement-6939.test.ts`. Quoted here from the
|
|
177
|
+
* `ObjectMapSchema` faces, whose `getDataConfig` is `ObjectMap.tsx`'s local
|
|
178
|
+
* wrapper around this function; the gantt and calendar faces name
|
|
179
|
+
* `resolveRecordSourceConfig` instead (objectui#9618):
|
|
180
|
+
*
|
|
181
|
+
* 1. **`data`** — *"Data source configuration. Read FIRST by `getDataConfig`"*,
|
|
182
|
+
* honoured ONLY on the arm `dataArm` names. Returned verbatim, so a config
|
|
183
|
+
* on the declared arm reaches the caller exactly as the author wrote it.
|
|
184
|
+
* 2. **`staticData`** — *"Inline records — read SECOND by `getDataConfig`,
|
|
185
|
+
* wrapped into a `{ provider: value }` config"*.
|
|
186
|
+
* 3. **`objectName`** — *"the THIRD record source `getDataConfig` resolves,
|
|
187
|
+
* after `data` and `staticData`"*, folded to `{ provider: 'object' }`.
|
|
188
|
+
*
|
|
189
|
+
* `null` when none of the three is present — the same "nothing is drawn" signal
|
|
190
|
+
* the zod `requireRecordSource` refinement is written against.
|
|
191
|
+
*
|
|
192
|
+
* This is the PRODUCER whose output {@link resolveRecordSourceObjectName} (the
|
|
193
|
+
* reader `b041b9c0c` published) consumes; that function's docblock describes the same
|
|
194
|
+
* ladder from the consuming end. Five plugins — calendar, gantt, grid, map and
|
|
195
|
+
* tree — each carried a hand-copy of this ladder with no gate holding them
|
|
196
|
+
* together, which is the AGENTS.md #0.1 drift class: a change to the ruled
|
|
197
|
+
* order had five edit sites and nothing noticed a missed one.
|
|
198
|
+
*
|
|
199
|
+
* ## Rung 1 is judged against the block's own row (objectui#8348)
|
|
200
|
+
*
|
|
201
|
+
* ⛔ This docblock used to state, as a fact about the whole ladder, that *"an
|
|
202
|
+
* array under `data` cannot be published"*. That is true of the blocks whose row
|
|
203
|
+
* is `ViewData` and FALSE of `object-calendar`, whose published row
|
|
204
|
+
* (`ComponentPropsMap['object-calendar'].data` on `@objectstack/spec` 17.4.0) is
|
|
205
|
+
* `z.array(z.unknown()).optional()` — *"Pre-fetched records — skips the internal
|
|
206
|
+
* fetch"*. Both directions of that disagreement were live at once: the renderers
|
|
207
|
+
* honoured the `{ provider, items }` object on a block whose row refuses it by
|
|
208
|
+
* kind, and `ObjectGrid` / `ObjectMap` lifted a bare array on blocks whose row
|
|
209
|
+
* refuses THAT by kind. Decision batch #83 settled it — the row decides — and
|
|
210
|
+
* `dataArm` is where each block says which row it has.
|
|
211
|
+
*
|
|
212
|
+
* JUDGED, per block, against that block's `ComponentPropsMap` row in the
|
|
213
|
+
* INSTALLED `@objectstack/spec` — and ⛔ the verdicts are not written down here
|
|
214
|
+
* as facts, because a spec release can move a row (AGENTS.md #9). Each pin
|
|
215
|
+
* below derives the arm from the row at test time and compares it with what the
|
|
216
|
+
* block passes, so a moved row turns it red:
|
|
217
|
+
*
|
|
218
|
+
* - `object-grid` — `'view-data'`; `gridBareArrayDataRefused-8348.test.tsx`,
|
|
219
|
+
* its "the spec row is what all of the above is judged against" test. The
|
|
220
|
+
* row's own description names the refusal: *"the bare-array shortcut is
|
|
221
|
+
* refused — see migration `object-grid-data-view-data-converged`"*.
|
|
222
|
+
* - `object-calendar` — `'array'`; the test of the same name in
|
|
223
|
+
* `ObjectCalendar.dataObjectArmRefused-8348.test.tsx`. The registration
|
|
224
|
+
* publishes the same arm (`{ name: 'data', type: 'array' }`).
|
|
225
|
+
* - `object-map`, `object-gantt`, `object-tree` — `'view-data'`; one
|
|
226
|
+
* `dataArmSpecRow-8348` pin per block, in each plugin's own tests. These
|
|
227
|
+
* three rows are what ruling batch #136 item 3 (Q1-C) had the protocol
|
|
228
|
+
* gain; until they were installable, map and gantt were judged through this
|
|
229
|
+
* repo's own `ObjectMapSchema.data` / `ObjectGanttSchema.data` and tree
|
|
230
|
+
* passed `'undeclared'`. The tree's row declares `data`, so the ruling's
|
|
231
|
+
* 「协议没有的能力删」 clause did not fire for it: it keeps the object arm and
|
|
232
|
+
* loses only the bare-array spelling. Its `staticData` rung is declared by
|
|
233
|
+
* the same row and is unchanged.
|
|
234
|
+
*
|
|
235
|
+
* ## The DECLARATION follows the arm too (`ab856ed30`)
|
|
236
|
+
*
|
|
237
|
+
* The `data` member below is {@link AuthoredRecordSourceData}`<Arm>`, not a flat
|
|
238
|
+
* `ViewData`: the arm decides what a site may DECLARE exactly as it decides
|
|
239
|
+
* what rung 1 HONOURS. Read that type's docblock for why it is arm-indexed
|
|
240
|
+
* rather than a union of the arms, and for the reading that says nothing at
|
|
241
|
+
* runtime moved.
|
|
242
|
+
*
|
|
243
|
+
* ## No lenient rung was added (AGENTS.md #0.1)
|
|
244
|
+
*
|
|
245
|
+
* **Null tolerance** stays out: all five copies dereference `schema` unguarded
|
|
246
|
+
* and would throw on `null`; no site passes one, so no `?.` was added.
|
|
247
|
+
*
|
|
248
|
+
* `ObjectCalendar`'s copy guarded with `'data' in schema && schema.data`. That
|
|
249
|
+
* `in` test was a TYPESCRIPT narrowing device, not a behavioural one: when the
|
|
250
|
+
* property is absent the read yields `undefined`, which is falsy either way, so
|
|
251
|
+
* the guard could never change which rung is taken. The optional-property
|
|
252
|
+
* parameter below admits a schema that declares neither key directly, which is
|
|
253
|
+
* why the guard is gone rather than flattened away. The equivalence itself is
|
|
254
|
+
* pinned, not argued, by `CALENDAR_IN_GUARD` in
|
|
255
|
+
* `record-source-config.behaviourNeutrality-7632.test.ts`.
|
|
256
|
+
*
|
|
257
|
+
* ⛔ This passage used to explain that guard by the PROPS UNION the calendar
|
|
258
|
+
* plugin once carried — `ObjectGridSchema | CalendarSchema`, whose second
|
|
259
|
+
* member declared neither `data` nor `staticData`. objectui#8651 RETIRED that
|
|
260
|
+
* union (`ObjectCalendarComponentProps.schema` is the published
|
|
261
|
+
* `ObjectCalendarSchema` today), so the sentence explained the guard by a type
|
|
262
|
+
* no reader could look up. Rewritten rather than deleted: the guard's history
|
|
263
|
+
* is still why this parameter's members are optional.
|
|
264
|
+
*
|
|
265
|
+
* @param schema - The block's schema; only `data`, `staticData` and
|
|
266
|
+
* `objectName` are read. Its `data` member is typed by the arm — see
|
|
267
|
+
* {@link AuthoredRecordSourceData}.
|
|
268
|
+
* @param dataArm - The arm the CALLING BLOCK's published `data` row declares.
|
|
269
|
+
* Required: there is no repo-wide default, because the answer differs per
|
|
270
|
+
* block and a default is how the second de-facto contract got in.
|
|
271
|
+
* @returns The resolved data config, or `null` when nothing is bound.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```ts
|
|
275
|
+
* const dataConfig = useMemo(() => resolveRecordSourceConfig(schema, 'view-data'), [schema]);
|
|
276
|
+
* const objectName = resolveRecordSourceObjectName(schema, dataConfig);
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
export declare function resolveRecordSourceConfig<Arm extends RecordSourceDataArm>(schema: {
|
|
280
|
+
objectName?: string;
|
|
281
|
+
data?: AuthoredRecordSourceData<Arm>;
|
|
282
|
+
staticData?: any[];
|
|
283
|
+
}, dataArm: Arm): ViewData | null;
|
|
284
|
+
/**
|
|
285
|
+
* Which `data` arm the block registered under `type` declares.
|
|
286
|
+
*
|
|
287
|
+
* `'undeclared'` for every type not listed above — the same verdict the ladder
|
|
288
|
+
* gives a block no published face declares a `data` row for, and the same
|
|
289
|
+
* behaviour those types have today. That default is deliberately the
|
|
290
|
+
* NO-CHANGE direction: a caller acting on this reading (today:
|
|
291
|
+
* `SchemaRenderer`'s props spread) leaves an unlisted type exactly as it was,
|
|
292
|
+
* so a block that has never been measured is never silently re-decided by this
|
|
293
|
+
* table's mere existence.
|
|
294
|
+
*
|
|
295
|
+
* @param type - The node's registry key, either spelling.
|
|
296
|
+
* @returns The arm that type's published `data` row declares.
|
|
297
|
+
*/
|
|
298
|
+
export declare function recordSourceDataArmForType(type: string | null | undefined): RecordSourceDataArm;
|