@object-ui/core 17.5.0 → 17.7.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 +7404 -0
- package/README.md +127 -26
- package/dist/actions/ActionEngine.d.ts +5 -3
- package/dist/actions/ActionEngine.js +14 -10
- package/dist/actions/ActionRunner.d.ts +363 -30
- package/dist/actions/ActionRunner.js +544 -58
- package/dist/actions/TransactionManager.d.ts +7 -2
- package/dist/actions/actionKeys.d.ts +75 -13
- package/dist/actions/actionKeys.js +131 -16
- package/dist/actions/actionResponse.js +3 -3
- package/dist/actions/index.d.ts +2 -0
- package/dist/actions/index.js +2 -0
- package/dist/actions/recordDelete.d.ts +118 -0
- package/dist/actions/recordDelete.js +112 -0
- package/dist/actions/recordIdParam.d.ts +77 -0
- package/dist/actions/recordIdParam.js +37 -0
- package/dist/actions/serverActionHandler.d.ts +1 -1
- package/dist/actions/serverActionHandler.js +1 -1
- package/dist/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/builder/schema-builder.d.ts +38 -81
- package/dist/builder/schema-builder.js +33 -152
- package/dist/data-scope/ViewDataProvider.d.ts +1 -1
- package/dist/data-scope/ViewDataProvider.js +21 -1
- 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 +128 -1
- package/dist/evaluator/ExpressionEvaluator.js +226 -13
- package/dist/evaluator/FormulaFunctions.d.ts +0 -10
- package/dist/evaluator/FormulaFunctions.js +166 -27
- package/dist/evaluator/SafeExpressionParser.js +0 -4
- package/dist/evaluator/declaredPredicate.d.ts +93 -9
- package/dist/evaluator/declaredPredicate.js +118 -17
- package/dist/evaluator/fieldRules.d.ts +107 -4
- package/dist/evaluator/fieldRules.js +231 -35
- package/dist/evaluator/index.d.ts +2 -0
- package/dist/evaluator/index.js +2 -0
- package/dist/evaluator/listConditional.d.ts +125 -9
- package/dist/evaluator/listConditional.js +167 -26
- package/dist/evaluator/optionRules.d.ts +126 -0
- package/dist/evaluator/optionRules.js +141 -4
- package/dist/evaluator/predicateInput.d.ts +15 -4
- package/dist/evaluator/predicateInput.js +35 -2
- package/dist/evaluator/rowPredicateCanon.d.ts +56 -0
- package/dist/evaluator/rowPredicateCanon.js +158 -0
- package/dist/evaluator/subjectPermissions.d.ts +76 -0
- package/dist/evaluator/subjectPermissions.js +44 -0
- package/dist/evaluator/unevaluablePredicate.d.ts +57 -0
- package/dist/evaluator/unevaluablePredicate.js +70 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +97 -2
- package/dist/protocols/index.d.ts +0 -1
- package/dist/protocols/index.js +9 -1
- package/dist/registry/Registry.d.ts +397 -48
- package/dist/registry/Registry.js +263 -11
- 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/theme/index.d.ts +1 -1
- package/dist/theme/index.js +1 -1
- package/dist/types/index.d.ts +92 -13
- 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 +188 -0
- package/dist/utils/chart-presentation.js +345 -0
- package/dist/utils/chart-series.d.ts +271 -31
- package/dist/utils/chart-series.js +478 -55
- package/dist/utils/column-identity.d.ts +43 -0
- package/dist/utils/column-identity.js +47 -1
- 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 +407 -52
- package/dist/utils/date-display.d.ts +354 -0
- package/dist/utils/date-display.js +463 -0
- package/dist/utils/date-macros.js +7 -0
- package/dist/utils/debug.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 +171 -11
- package/dist/utils/expand-fields.js +171 -11
- package/dist/utils/extract-records.d.ts +65 -5
- package/dist/utils/extract-records.js +65 -8
- package/dist/utils/filter-converter.d.ts +152 -4
- package/dist/utils/filter-converter.js +1092 -22
- package/dist/utils/filter-tokens.d.ts +49 -5
- package/dist/utils/filter-tokens.js +105 -34
- package/dist/utils/freeze-schema.d.ts +38 -1
- package/dist/utils/freeze-schema.js +10 -0
- package/dist/utils/grouping-fields.d.ts +61 -0
- package/dist/utils/grouping-fields.js +84 -0
- package/dist/utils/humanize-label.d.ts +60 -0
- package/dist/utils/humanize-label.js +62 -0
- package/dist/utils/managedBy.d.ts +30 -5
- 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 +131 -1
- package/dist/utils/normalize-list-view.js +248 -6
- package/dist/utils/number-display.d.ts +70 -17
- 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 +62 -4
- package/dist/utils/predicate-fields.js +169 -4
- 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/retired-field-types.d.ts +90 -0
- package/dist/utils/retired-field-types.js +124 -0
- package/dist/utils/sort-query.d.ts +128 -26
- 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/unmaterialized-fields.d.ts +52 -0
- package/dist/utils/unmaterialized-fields.js +57 -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 +9 -4
- package/dist/validation/index.js +9 -4
- package/dist/validation/schema-validator.d.ts +1 -1
- package/dist/validation/schema-validator.js +305 -82
- package/dist/validation/server-owned-value.d.ts +62 -0
- package/dist/validation/server-owned-value.js +99 -0
- package/dist/validation/validators/object-validation-engine.d.ts +1 -1
- package/dist/validation/validators/object-validation-engine.js +14 -5
- package/package.json +8 -9
- package/dist/__benchmarks__/core.bench.d.ts +0 -8
- package/dist/__benchmarks__/core.bench.js +0 -53
- 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
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI — retired field-type spellings
|
|
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
|
+
* TOMBSTONE table — field-type spellings this renderer has RETIRED, mapped to
|
|
10
|
+
* the prescription an author must follow instead (ADR-0049 enforce-or-remove).
|
|
11
|
+
*
|
|
12
|
+
* A retired spelling is not merely absent: absence here means
|
|
13
|
+
* `mapFieldTypeToFormType`'s `|| 'field:text'` tail would hand back a
|
|
14
|
+
* working plain text input, which is the failure mode this table exists to
|
|
15
|
+
* prevent. An author who writes a retired name — or an AI author who copies one
|
|
16
|
+
* out of a stale doc — must be TOLD, not quietly given a text box that looks
|
|
17
|
+
* like it worked. So each entry resolves to a registered tombstone widget that
|
|
18
|
+
* renders a visible refusal naming the migration
|
|
19
|
+
* (`packages/fields/src/index.tsx`), while {@link reportRetiredFieldType}
|
|
20
|
+
* writes the same prescription to the console.
|
|
21
|
+
*
|
|
22
|
+
* `owner` (objectui#4814, ruling A′): a synonym for `user` with zero behavioral
|
|
23
|
+
* delta — both resolved to the SAME `UserField` widget — and absent from the
|
|
24
|
+
* spec's closed 48-member `FieldType`, so no object schema could ever declare
|
|
25
|
+
* it; it was reachable only through hand-written SDUI. Three code faces had
|
|
26
|
+
* already drifted apart on this one word (the form's data-source rule excluded
|
|
27
|
+
* it while plugin-grid's bulk dialog included it), which is the standing
|
|
28
|
+
* evidence that a second spelling for one concept is a drift channel, not a
|
|
29
|
+
* convenience. The idiom survives verbatim as `{ type: 'user', name: 'owner' }`.
|
|
30
|
+
*
|
|
31
|
+
* ## Why this table lives in `@object-ui/core` and not in `@object-ui/fields`
|
|
32
|
+
*
|
|
33
|
+
* It was born in `packages/fields/src/field-type-alias.ts`, next to the widget
|
|
34
|
+
* map that is its first consumer, and `@object-ui/fields` still exports every
|
|
35
|
+
* name in this module so no consumer of that surface had to move. It was HOISTED
|
|
36
|
+
* here by objectui#4914, and for a measured reason rather than a tidiness one:
|
|
37
|
+
* the retirement gate has to run ahead of six predicate faces, and one of them
|
|
38
|
+
* — `operatorsForFieldType` / the filter row's control choice in
|
|
39
|
+
* `packages/components/src/custom/filter-builder.tsx` — lives in
|
|
40
|
+
* `@object-ui/components`, which `@object-ui/fields` DEPENDS ON (54 source
|
|
41
|
+
* files import from it). A `components → fields` import is a package cycle, so
|
|
42
|
+
* the six faces could not all read one table while that table lived in
|
|
43
|
+
* `fields`.
|
|
44
|
+
*
|
|
45
|
+
* The alternative was a second copy of the table inside `components`, and that
|
|
46
|
+
* is precisely what this file exists to make impossible: two tables means two
|
|
47
|
+
* `reportedRetiredTypes` sets, so one retired spelling logs its prescription
|
|
48
|
+
* TWICE — and the "fires once" half of the ruling would be false the day it
|
|
49
|
+
* landed. `@object-ui/core` is below all six faces' packages (it imports only
|
|
50
|
+
* `@object-ui/types`), and already owns the sibling cross-package field-type
|
|
51
|
+
* vocabulary — {@link EXPANDABLE_FIELD_TYPES} in `./expand-fields.js`, whose
|
|
52
|
+
* own docblock records what happened the last time this kind of table was
|
|
53
|
+
* hand-copied per consumer. One table, one home, seven consumers.
|
|
54
|
+
*/
|
|
55
|
+
export declare const RETIRED_FIELD_TYPES: Readonly<Record<string, string>>;
|
|
56
|
+
/**
|
|
57
|
+
* THE retirement gate — true when a spelling has been retired by this renderer.
|
|
58
|
+
*
|
|
59
|
+
* Ruled into existence by the maintainer on 2026-08-18 (objectui#4914, option
|
|
60
|
+
* B) as the ONE question every field-type predicate asks before it answers:
|
|
61
|
+
* "a retired spelling fires `reportRetiredFieldType` once and takes the
|
|
62
|
+
* established disposition, instead of riding the lookup-family paths or
|
|
63
|
+
* silently degrading."
|
|
64
|
+
*
|
|
65
|
+
* Quantified over the whole table rather than written per spelling, which is
|
|
66
|
+
* the entire point of having a gate at all: the next retirement closes every
|
|
67
|
+
* face the day it lands in {@link RETIRED_FIELD_TYPES}, instead of leaving each
|
|
68
|
+
* consumer open until someone notices it. `owner` survived objectui#4814 in
|
|
69
|
+
* thirteen more faces exactly because each one had hand-written its own
|
|
70
|
+
* membership list.
|
|
71
|
+
*
|
|
72
|
+
* `hasOwnProperty`, not `RETIRED_FIELD_TYPES[type]`: a `type` of
|
|
73
|
+
* `'constructor'` or `'toString'` reaches `Object.prototype` and would answer
|
|
74
|
+
* truthy through a plain index read.
|
|
75
|
+
*
|
|
76
|
+
* @param type - The spelling an author wrote (or a backend column declared).
|
|
77
|
+
* @returns `true` when the spelling is retired. Pure — it does NOT log; pair it
|
|
78
|
+
* with {@link reportRetiredFieldType} at the face that wants to be loud.
|
|
79
|
+
*/
|
|
80
|
+
export declare function isRetiredFieldType(type: string | undefined | null): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Report a retired field-type spelling loudly, once per spelling.
|
|
83
|
+
*
|
|
84
|
+
* @param fieldType - The spelling the author wrote.
|
|
85
|
+
* @returns `true` when `fieldType` is retired (whether or not this call was the
|
|
86
|
+
* one that logged), so callers can branch on it without a second table lookup.
|
|
87
|
+
*/
|
|
88
|
+
export declare function reportRetiredFieldType(fieldType: string): boolean;
|
|
89
|
+
/** Test seam — forget which spellings have been reported. */
|
|
90
|
+
export declare function resetRetiredFieldTypeReports(): void;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI — retired field-type spellings
|
|
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
|
+
* TOMBSTONE table — field-type spellings this renderer has RETIRED, mapped to
|
|
10
|
+
* the prescription an author must follow instead (ADR-0049 enforce-or-remove).
|
|
11
|
+
*
|
|
12
|
+
* A retired spelling is not merely absent: absence here means
|
|
13
|
+
* `mapFieldTypeToFormType`'s `|| 'field:text'` tail would hand back a
|
|
14
|
+
* working plain text input, which is the failure mode this table exists to
|
|
15
|
+
* prevent. An author who writes a retired name — or an AI author who copies one
|
|
16
|
+
* out of a stale doc — must be TOLD, not quietly given a text box that looks
|
|
17
|
+
* like it worked. So each entry resolves to a registered tombstone widget that
|
|
18
|
+
* renders a visible refusal naming the migration
|
|
19
|
+
* (`packages/fields/src/index.tsx`), while {@link reportRetiredFieldType}
|
|
20
|
+
* writes the same prescription to the console.
|
|
21
|
+
*
|
|
22
|
+
* `owner` (objectui#4814, ruling A′): a synonym for `user` with zero behavioral
|
|
23
|
+
* delta — both resolved to the SAME `UserField` widget — and absent from the
|
|
24
|
+
* spec's closed 48-member `FieldType`, so no object schema could ever declare
|
|
25
|
+
* it; it was reachable only through hand-written SDUI. Three code faces had
|
|
26
|
+
* already drifted apart on this one word (the form's data-source rule excluded
|
|
27
|
+
* it while plugin-grid's bulk dialog included it), which is the standing
|
|
28
|
+
* evidence that a second spelling for one concept is a drift channel, not a
|
|
29
|
+
* convenience. The idiom survives verbatim as `{ type: 'user', name: 'owner' }`.
|
|
30
|
+
*
|
|
31
|
+
* ## Why this table lives in `@object-ui/core` and not in `@object-ui/fields`
|
|
32
|
+
*
|
|
33
|
+
* It was born in `packages/fields/src/field-type-alias.ts`, next to the widget
|
|
34
|
+
* map that is its first consumer, and `@object-ui/fields` still exports every
|
|
35
|
+
* name in this module so no consumer of that surface had to move. It was HOISTED
|
|
36
|
+
* here by objectui#4914, and for a measured reason rather than a tidiness one:
|
|
37
|
+
* the retirement gate has to run ahead of six predicate faces, and one of them
|
|
38
|
+
* — `operatorsForFieldType` / the filter row's control choice in
|
|
39
|
+
* `packages/components/src/custom/filter-builder.tsx` — lives in
|
|
40
|
+
* `@object-ui/components`, which `@object-ui/fields` DEPENDS ON (54 source
|
|
41
|
+
* files import from it). A `components → fields` import is a package cycle, so
|
|
42
|
+
* the six faces could not all read one table while that table lived in
|
|
43
|
+
* `fields`.
|
|
44
|
+
*
|
|
45
|
+
* The alternative was a second copy of the table inside `components`, and that
|
|
46
|
+
* is precisely what this file exists to make impossible: two tables means two
|
|
47
|
+
* `reportedRetiredTypes` sets, so one retired spelling logs its prescription
|
|
48
|
+
* TWICE — and the "fires once" half of the ruling would be false the day it
|
|
49
|
+
* landed. `@object-ui/core` is below all six faces' packages (it imports only
|
|
50
|
+
* `@object-ui/types`), and already owns the sibling cross-package field-type
|
|
51
|
+
* vocabulary — {@link EXPANDABLE_FIELD_TYPES} in `./expand-fields.js`, whose
|
|
52
|
+
* own docblock records what happened the last time this kind of table was
|
|
53
|
+
* hand-copied per consumer. One table, one home, seven consumers.
|
|
54
|
+
*/
|
|
55
|
+
export const RETIRED_FIELD_TYPES = Object.freeze({
|
|
56
|
+
owner: "[object-ui] Field type `owner` was RETIRED (objectui#4814). It was a synonym " +
|
|
57
|
+
"for `user` with no behavioral difference, and it is not a member of " +
|
|
58
|
+
"`@objectstack/spec`'s FieldType. Write the record-owner field as " +
|
|
59
|
+
"`{ type: 'user', name: 'owner' }` — the field NAME carries the ownership " +
|
|
60
|
+
"meaning, the type carries the widget. The `widget: 'field:owner'` spelling " +
|
|
61
|
+
"is retired with it.",
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* THE retirement gate — true when a spelling has been retired by this renderer.
|
|
65
|
+
*
|
|
66
|
+
* Ruled into existence by the maintainer on 2026-08-18 (objectui#4914, option
|
|
67
|
+
* B) as the ONE question every field-type predicate asks before it answers:
|
|
68
|
+
* "a retired spelling fires `reportRetiredFieldType` once and takes the
|
|
69
|
+
* established disposition, instead of riding the lookup-family paths or
|
|
70
|
+
* silently degrading."
|
|
71
|
+
*
|
|
72
|
+
* Quantified over the whole table rather than written per spelling, which is
|
|
73
|
+
* the entire point of having a gate at all: the next retirement closes every
|
|
74
|
+
* face the day it lands in {@link RETIRED_FIELD_TYPES}, instead of leaving each
|
|
75
|
+
* consumer open until someone notices it. `owner` survived objectui#4814 in
|
|
76
|
+
* thirteen more faces exactly because each one had hand-written its own
|
|
77
|
+
* membership list.
|
|
78
|
+
*
|
|
79
|
+
* `hasOwnProperty`, not `RETIRED_FIELD_TYPES[type]`: a `type` of
|
|
80
|
+
* `'constructor'` or `'toString'` reaches `Object.prototype` and would answer
|
|
81
|
+
* truthy through a plain index read.
|
|
82
|
+
*
|
|
83
|
+
* @param type - The spelling an author wrote (or a backend column declared).
|
|
84
|
+
* @returns `true` when the spelling is retired. Pure — it does NOT log; pair it
|
|
85
|
+
* with {@link reportRetiredFieldType} at the face that wants to be loud.
|
|
86
|
+
*/
|
|
87
|
+
export function isRetiredFieldType(type) {
|
|
88
|
+
if (typeof type !== 'string' || !type)
|
|
89
|
+
return false;
|
|
90
|
+
return Object.prototype.hasOwnProperty.call(RETIRED_FIELD_TYPES, type);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Spellings already reported this session, so a retired type inside a rendered
|
|
94
|
+
* list logs its prescription ONCE instead of once per row. The message is a
|
|
95
|
+
* fix instruction for an author, not a per-render event — a 1000-row grid
|
|
96
|
+
* repeating it 1000 times buries the very thing it is trying to surface.
|
|
97
|
+
*
|
|
98
|
+
* Module state, and that is load bearing now that SEVEN faces call the reporter
|
|
99
|
+
* (objectui#4914): the dedupe is per SPELLING, not per face, so a filter row
|
|
100
|
+
* whose column is refused by `operatorsForFieldType`, by the control chooser
|
|
101
|
+
* and by `normalizeFieldType` on the same render still prints one line.
|
|
102
|
+
*/
|
|
103
|
+
const reportedRetiredTypes = new Set();
|
|
104
|
+
/**
|
|
105
|
+
* Report a retired field-type spelling loudly, once per spelling.
|
|
106
|
+
*
|
|
107
|
+
* @param fieldType - The spelling the author wrote.
|
|
108
|
+
* @returns `true` when `fieldType` is retired (whether or not this call was the
|
|
109
|
+
* one that logged), so callers can branch on it without a second table lookup.
|
|
110
|
+
*/
|
|
111
|
+
export function reportRetiredFieldType(fieldType) {
|
|
112
|
+
const prescription = isRetiredFieldType(fieldType) ? RETIRED_FIELD_TYPES[fieldType] : undefined;
|
|
113
|
+
if (!prescription)
|
|
114
|
+
return false;
|
|
115
|
+
if (!reportedRetiredTypes.has(fieldType)) {
|
|
116
|
+
reportedRetiredTypes.add(fieldType);
|
|
117
|
+
console.error(prescription);
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
/** Test seam — forget which spellings have been reported. */
|
|
122
|
+
export function resetRetiredFieldTypeReports() {
|
|
123
|
+
reportedRetiredTypes.clear();
|
|
124
|
+
}
|
|
@@ -9,47 +9,149 @@
|
|
|
9
9
|
* sort-query — lower a block's authored `sort` to the `$orderby` value a
|
|
10
10
|
* `DataSource.find` query carries.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
12
|
+
* ONE authored spelling reaches every object-bound block: the spec's
|
|
13
|
+
* `SortConfig[]` (`[{ field, order }]`). The legacy OData-ish string clause
|
|
14
|
+
* (`"name desc"`) that this function also honoured until objectui#8221 is
|
|
15
|
+
* RETIRED — director ruling, decision batch #77, 2026-09-07 (objectui#8221,
|
|
16
|
+
* maintainer approved), option B: "The platform has one `sort` spelling, the
|
|
17
|
+
* array, everywhere."
|
|
18
|
+
*
|
|
19
|
+
* Why the string had to go rather than be declared alongside the array: every
|
|
20
|
+
* `sort` input in the registry publishes `type: 'array'` alone, so the html
|
|
21
|
+
* tier already answered `type-mismatch` for the string, and
|
|
22
|
+
* `@objectstack/spec` refuses it outright on `element-record-picker`. A
|
|
23
|
+
* spelling that core implements, the docs teach and the validator rejects is
|
|
24
|
+
* three faces disagreeing, and the ruling settled which one wins. Declaring
|
|
25
|
+
* per-block string arms instead (option A) was rejected by name: it would make
|
|
26
|
+
* one key mean different things on different blocks.
|
|
27
|
+
*
|
|
28
|
+
* This is now the ONLY definition in the repo. It was hoisted here because
|
|
29
|
+
* objectstack#7137 added two more read sites (`object-timeline`,
|
|
30
|
+
* `record:line_items`) next to three sibling blocks that each inlined a
|
|
31
|
+
* byte-identical private copy (`ObjectGantt` / `ObjectMap` / `ObjectCalendar`),
|
|
32
|
+
* and a fifth and sixth copy is how the conversions start disagreeing. Those
|
|
33
|
+
* three copies were deliberately left alone by objectstack-ai/objectstack#7137 and collapsed onto this
|
|
34
|
+
* function by objectui#4022; every block now imports it from here.
|
|
35
|
+
*
|
|
36
|
+
* Two deliberate differences from those retired private copies, both of which
|
|
37
|
+
* make this function more faithful to the declared contract rather than adding
|
|
38
|
+
* tolerance — and both are BEHAVIOUR CHANGES the migration delivered, not pure
|
|
39
|
+
* refactor:
|
|
40
|
+
*
|
|
41
|
+
* - **A missing `order` is READ as ascending rather than dropped.** ⚠️ This
|
|
42
|
+
* bullet used to say `order` is "optional in `SortConfig`". It is not:
|
|
43
|
+
* `SortConfig.order` is required on the interface, on its zod mirror, and on
|
|
44
|
+
* `@objectstack/spec`'s `SortItemSchema`, which refuses an entry without it
|
|
45
|
+
* (`invalid_value` at `0.order`, measured on `@objectstack/spec@17.4.0`).
|
|
46
|
+
* Corrected under objectui#8973, which is where objectui#8767's contract
|
|
47
|
+
* review routed this sentence and its twin in
|
|
48
|
+
* `@object-ui/types`' `ObjectGridSchema.sort` docblock. The tolerance is
|
|
49
|
+
* real but it is a RUNTIME one — types are erased, so an entry missing
|
|
50
|
+
* `order` still arrives and still has to mean something. The private copies
|
|
51
|
+
* required BOTH keys and silently dropped such an entry, which lost an
|
|
34
52
|
* authored sort key instead of ordering by it.
|
|
35
53
|
* - **Nothing usable yields `undefined`, never `{}`.** An empty object is a
|
|
36
54
|
* truthy value that means "no ordering" only by accident of the adapter's
|
|
37
55
|
* serializer; `undefined` says it, so the query simply carries no `$orderby`.
|
|
38
56
|
*
|
|
39
57
|
* Not a lenient alias: an unusable input (a number, an object, an array of
|
|
40
|
-
* strings) yields `undefined` rather than a guess. Only the
|
|
41
|
-
* schema types declare
|
|
58
|
+
* strings) yields `undefined` rather than a guess. Only the ONE spelling the
|
|
59
|
+
* schema types declare is honoured.
|
|
60
|
+
*
|
|
61
|
+
* ⚠️ A retired string is REFUSED OUT LOUD, not dropped in silence. Types are
|
|
62
|
+
* erased, so the narrowed signature below cannot stop a string: authored JSON,
|
|
63
|
+
* a stored `sys_metadata` row and an `as any` bag all reach this function
|
|
64
|
+
* unparsed. Returning `undefined` and saying nothing would turn every one of
|
|
65
|
+
* those into a row order that quietly stopped applying — the exact failure this
|
|
66
|
+
* repository has measured over and over. See {@link convertSortToQueryParams}.
|
|
67
|
+
*
|
|
68
|
+
* ⚠️ OUT LOUD is about the SPELLING, not about the container it arrives in
|
|
69
|
+
* (objectui#9955). `sort: ['name asc']` is the same retired clause one level
|
|
70
|
+
* down, and it used to reach the array arm's `continue` and leave through the
|
|
71
|
+
* `undefined` return with nothing printed — the loud scalar refusal sat one arm
|
|
72
|
+
* above it, so the two spellings of one mistake got opposite treatment. Both are
|
|
73
|
+
* loud now. ⛔ Being loud is NOT accepting: the sentence four paragraphs up
|
|
74
|
+
* ("an unusable input … yields `undefined` rather than a guess") still holds
|
|
75
|
+
* byte for byte, and `['name asc']` still contributes no ordering. The ruling
|
|
76
|
+
* quoted at the top of this docblock — one `sort` spelling, the array,
|
|
77
|
+
* everywhere — is the reason making it WORK was never on the table.
|
|
42
78
|
*/
|
|
43
79
|
/** A field name paired with a direction — the spec's `SortConfig`. */
|
|
44
80
|
export interface QuerySortEntry {
|
|
45
81
|
field?: string;
|
|
46
82
|
order?: 'asc' | 'desc';
|
|
47
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Test seam — forget which retired spellings have been reported.
|
|
86
|
+
*
|
|
87
|
+
* Needed because the dedupe above is module state: without it the second test
|
|
88
|
+
* to assert the refusal diagnostic would observe silence and pass for the
|
|
89
|
+
* wrong reason.
|
|
90
|
+
*/
|
|
91
|
+
export declare function resetRetiredSortSpellingReports(): void;
|
|
92
|
+
/**
|
|
93
|
+
* A sort entry after normalization — BOTH keys present, always. That is the
|
|
94
|
+
* difference from {@link QuerySortEntry}, whose two members are optional
|
|
95
|
+
* because it describes what an author may WRITE, not what survives this door.
|
|
96
|
+
*/
|
|
97
|
+
export interface NormalizedSortEntry {
|
|
98
|
+
field: string;
|
|
99
|
+
order: 'asc' | 'desc';
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Decide WHICH authored entries survive and WHAT a missing direction means —
|
|
103
|
+
* the whole of this module's normalization, with no opinion about the wire.
|
|
104
|
+
*
|
|
105
|
+
* Split out of {@link convertSortToQueryParams} by objectui#8973, which measured
|
|
106
|
+
* `object-grid` re-implementing this decision privately and getting it wrong:
|
|
107
|
+
* its array arm interpolated every key unconditionally, so an entry missing
|
|
108
|
+
* `field` or `order` reached the wire as the literal text `undefined`
|
|
109
|
+
* (`$orderby: 'name undefined'`), which the server answers `400 INVALID_QUERY`.
|
|
110
|
+
*
|
|
111
|
+
* ⚠️ It is SHAPE-AGNOSTIC on purpose, and that is what makes it reusable where
|
|
112
|
+
* the map below is not. `object-grid` sends a `"field order"` join string, not
|
|
113
|
+
* this module's `{field: direction}` map; swapping its wire shape is route B on
|
|
114
|
+
* objectui#8767, which the maintainer DECLINED by name (2026-09-10) pending a
|
|
115
|
+
* card that measures the server contract and both readers. Exporting the
|
|
116
|
+
* decision without the map lets that block share the ONE implementation of the
|
|
117
|
+
* rule while keeping the wire shape the declination protects — so "one
|
|
118
|
+
* operation, one implementation" is satisfied for the operation that actually
|
|
119
|
+
* has two copies, rather than being traded away for a shape change nobody
|
|
120
|
+
* ruled on.
|
|
121
|
+
*
|
|
122
|
+
* The two rules, unchanged from the map builder they were lifted out of:
|
|
123
|
+
*
|
|
124
|
+
* - an entry with no usable `field` is SKIPPED (it names nothing to order by);
|
|
125
|
+
* - `order` normalizes to `'asc'` unless it is exactly `'desc'`, so a missing
|
|
126
|
+
* direction means ascending and a garbage one does not reach the wire.
|
|
127
|
+
*
|
|
128
|
+
* A third rule was added by objectui#9955 and it changes no RETURN value: a
|
|
129
|
+
* STRING entry is the retired clause one container deeper, so it is now REFUSED
|
|
130
|
+
* OUT LOUD before the skip above reaches it — and then skipped, contributing
|
|
131
|
+
* nothing, exactly as before. ⛔ The two rules above are untouched by it; in
|
|
132
|
+
* particular the entry-shaped object with no `field` keeps its DELIBERATE
|
|
133
|
+
* silence, because it names nothing to order by rather than naming it in a
|
|
134
|
+
* retired spelling. The seam is `typeof entry === 'string'` and only that.
|
|
135
|
+
*
|
|
136
|
+
* @returns The surviving entries in authored order, or `undefined` when nothing
|
|
137
|
+
* orderable was authored — never an empty array, so callers can omit the
|
|
138
|
+
* query key entirely rather than asking for an ordering with no content.
|
|
139
|
+
*/
|
|
140
|
+
export declare function normalizeSortEntries(sort: QuerySortEntry[] | undefined | null): NormalizedSortEntry[] | undefined;
|
|
48
141
|
/**
|
|
49
142
|
* Normalize an authored `sort` into the field→direction map used for
|
|
50
143
|
* `QueryParams.$orderby`.
|
|
51
144
|
*
|
|
52
|
-
*
|
|
145
|
+
* The decision about which entries survive lives in
|
|
146
|
+
* {@link normalizeSortEntries}; this function is only the MAP projection of it.
|
|
147
|
+
* Duplicate fields therefore collapse last-wins, keeping the position of the
|
|
148
|
+
* first mention — the same thing the single-pass loop this replaced did, since
|
|
149
|
+
* an object key keeps its insertion position when it is re-assigned.
|
|
150
|
+
*
|
|
151
|
+
* @param sort `SortConfig[]` — the one declared spelling. The legacy string
|
|
152
|
+
* clause (`"name desc"`) is retired (objectui#8221): a string that reaches here
|
|
153
|
+
* at runtime is refused with a diagnostic naming the array form, and this
|
|
154
|
+
* function returns `undefined` so the query carries no `$orderby`.
|
|
53
155
|
* @returns The ordering map, or `undefined` when nothing orderable was authored.
|
|
54
156
|
*/
|
|
55
|
-
export declare function convertSortToQueryParams(sort:
|
|
157
|
+
export declare function convertSortToQueryParams(sort: QuerySortEntry[] | undefined | null): Record<string, 'asc' | 'desc'> | undefined;
|