@object-ui/core 17.7.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 +650 -0
- package/README.md +127 -0
- package/dist/actions/ActionRunner.d.ts +192 -49
- package/dist/actions/ActionRunner.js +389 -102
- package/dist/actions/UndoManager.d.ts +8 -13
- package/dist/actions/actionKeys.d.ts +1 -1
- package/dist/actions/actionKeys.js +10 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/actions/navigationTarget.d.ts +41 -0
- package/dist/actions/navigationTarget.js +20 -0
- package/dist/actions/recordDelete.d.ts +59 -23
- package/dist/actions/recordDelete.js +40 -42
- package/dist/adapters/ApiDataSource.d.ts +8 -4
- package/dist/adapters/ApiDataSource.js +19 -7
- package/dist/adapters/resolveDataSource.d.ts +9 -6
- package/dist/adapters/resolveDataSource.js +9 -12
- package/dist/data-scope/ViewDataProvider.d.ts +7 -24
- package/dist/data-scope/ViewDataProvider.js +6 -53
- package/dist/evaluator/deadlineSettled.d.ts +40 -0
- package/dist/evaluator/deadlineSettled.js +45 -0
- package/dist/evaluator/fieldRules.js +6 -1
- package/dist/evaluator/index.d.ts +1 -0
- package/dist/evaluator/index.js +1 -0
- package/dist/evaluator/optionRules.d.ts +1 -1
- package/dist/evaluator/optionRules.js +1 -1
- package/dist/evaluator/subjectPermissions.d.ts +30 -1
- package/dist/evaluator/subjectPermissions.js +42 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/registry/Registry.d.ts +34 -13
- package/dist/registry/Registry.js +5 -22
- package/dist/utils/affordanceGrants.d.ts +371 -0
- package/dist/utils/affordanceGrants.js +252 -0
- package/dist/utils/chart-series.d.ts +34 -0
- package/dist/utils/chart-series.js +25 -2
- package/dist/utils/date-display.d.ts +42 -50
- package/dist/utils/date-display.js +192 -11
- package/dist/utils/expand-fields.d.ts +6 -4
- package/dist/utils/expand-fields.js +6 -4
- package/dist/utils/filter-converter.d.ts +6 -2
- package/dist/utils/filter-converter.js +6 -2
- package/dist/utils/normalize-list-view.d.ts +16 -12
- package/dist/utils/normalize-list-view.js +66 -67
- package/dist/utils/predicate-fields.js +6 -2
- package/dist/utils/record-source.d.ts +1 -1
- package/dist/utils/record-source.js +1 -1
- package/dist/utils/reference-keys.js +1 -1
- package/dist/validation/schema-validator.js +29 -5
- package/dist/validation/server-owned-value.d.ts +5 -1
- package/dist/validation/server-owned-value.js +5 -1
- package/package.json +4 -4
|
@@ -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
24
|
import { collectSavedViews, composeElementDataSource, elementDataSourceRefusedLimitMessage, 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
|
-
}
|
|
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) {
|
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
* Whether a declared deadline is settled for one row (objectui#11815).
|
|
10
|
+
*
|
|
11
|
+
* `@objectstack/spec` declares the deadline semantic on a `date` / `datetime`
|
|
12
|
+
* field: `dueLike: true` makes it a deadline, and `settledWhen` is a per-record
|
|
13
|
+
* CEL predicate that, while it holds, settles that deadline for the record —
|
|
14
|
+
* no overdue wording, no overdue colour. The date cells paint from the field's
|
|
15
|
+
* declaration alone and cannot evaluate a predicate, so every host that draws
|
|
16
|
+
* one beside a row asks THIS function and hands the cell its answer as the
|
|
17
|
+
* `deadlineSettled` prop. One helper, so no host carries its own copy.
|
|
18
|
+
*/
|
|
19
|
+
import { type RowPredicateOptions } from './listConditional.js';
|
|
20
|
+
import type { FieldRulePredicate } from './fieldRules.js';
|
|
21
|
+
/** The two declared keys {@link isDeadlineSettled} reads off a field. */
|
|
22
|
+
export interface DeadlineFieldLike {
|
|
23
|
+
name?: string;
|
|
24
|
+
dueLike?: boolean | null;
|
|
25
|
+
settledWhen?: FieldRulePredicate | null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* `true` only when the field declares `dueLike: true` AND a `settledWhen`, and
|
|
29
|
+
* that predicate holds for `row` (bound as `record`, through
|
|
30
|
+
* {@link evalRowPredicate} — the evaluator, scope and relation collapse the
|
|
31
|
+
* grid's conditional formatting uses).
|
|
32
|
+
*
|
|
33
|
+
* Every other answer is `false`:
|
|
34
|
+
* - a field that is not a declared deadline, or declares no `settledWhen`, is
|
|
35
|
+
* never settled;
|
|
36
|
+
* - a predicate that fails to evaluate is NOT settled. It was judged when it
|
|
37
|
+
* was authored, and a quiet "settled" would hide an overdue item; the
|
|
38
|
+
* evaluator's own one-time warning names the fault.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isDeadlineSettled(field: DeadlineFieldLike | null | undefined, row: Record<string, unknown> | null | undefined, opts?: Pick<RowPredicateOptions, 'scope' | 'fields'>): boolean;
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
* Whether a declared deadline is settled for one row (objectui#11815).
|
|
10
|
+
*
|
|
11
|
+
* `@objectstack/spec` declares the deadline semantic on a `date` / `datetime`
|
|
12
|
+
* field: `dueLike: true` makes it a deadline, and `settledWhen` is a per-record
|
|
13
|
+
* CEL predicate that, while it holds, settles that deadline for the record —
|
|
14
|
+
* no overdue wording, no overdue colour. The date cells paint from the field's
|
|
15
|
+
* declaration alone and cannot evaluate a predicate, so every host that draws
|
|
16
|
+
* one beside a row asks THIS function and hands the cell its answer as the
|
|
17
|
+
* `deadlineSettled` prop. One helper, so no host carries its own copy.
|
|
18
|
+
*/
|
|
19
|
+
import { evalRowPredicate } from './listConditional.js';
|
|
20
|
+
/**
|
|
21
|
+
* `true` only when the field declares `dueLike: true` AND a `settledWhen`, and
|
|
22
|
+
* that predicate holds for `row` (bound as `record`, through
|
|
23
|
+
* {@link evalRowPredicate} — the evaluator, scope and relation collapse the
|
|
24
|
+
* grid's conditional formatting uses).
|
|
25
|
+
*
|
|
26
|
+
* Every other answer is `false`:
|
|
27
|
+
* - a field that is not a declared deadline, or declares no `settledWhen`, is
|
|
28
|
+
* never settled;
|
|
29
|
+
* - a predicate that fails to evaluate is NOT settled. It was judged when it
|
|
30
|
+
* was authored, and a quiet "settled" would hide an overdue item; the
|
|
31
|
+
* evaluator's own one-time warning names the fault.
|
|
32
|
+
*/
|
|
33
|
+
export function isDeadlineSettled(field, row, opts = {}) {
|
|
34
|
+
if (!field || field.dueLike !== true)
|
|
35
|
+
return false;
|
|
36
|
+
const settledWhen = field.settledWhen;
|
|
37
|
+
if (settledWhen == null)
|
|
38
|
+
return false;
|
|
39
|
+
return evalRowPredicate(settledWhen, row, {
|
|
40
|
+
fallback: false,
|
|
41
|
+
scope: opts.scope,
|
|
42
|
+
fields: opts.fields,
|
|
43
|
+
label: `${field.name ?? 'field'}.settledWhen`,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
* site as every other fault; the VERDICT is unchanged for every input.
|
|
75
75
|
*/
|
|
76
76
|
import { ExpressionEngine } from '@objectstack/formula';
|
|
77
|
-
import { subjectPermissionsOf } from './subjectPermissions.js';
|
|
77
|
+
import { subjectCapabilitiesOf, subjectPermissionsOf } from './subjectPermissions.js';
|
|
78
78
|
import { isBlankPredicateText } from './declaredPredicate.js';
|
|
79
79
|
import { isUnevaluablePredicate, UNEVALUABLE_PREDICATE_REASON } from './unevaluablePredicate.js';
|
|
80
80
|
/** Normalize a predicate into the `Expression` shape the engine expects. */
|
|
@@ -226,11 +226,16 @@ export function evalFieldPredicate(pred, record, fallback, previous, scope, diag
|
|
|
226
226
|
// with none passes none, and `can` then refuses loudly: that fault goes
|
|
227
227
|
// through the caller's `fallback` like any other.
|
|
228
228
|
const permissions = subjectPermissionsOf(scope?.current_user);
|
|
229
|
+
// objectui#13006 — the subject's held system capabilities, the one input
|
|
230
|
+
// `current_user.hasCapability(name)` is answered from, carried the same
|
|
231
|
+
// way and for the same reason; absent stays absent, so the call refuses.
|
|
232
|
+
const systemPermissions = subjectCapabilitiesOf(scope?.current_user);
|
|
229
233
|
const res = ExpressionEngine.evaluate(expr, {
|
|
230
234
|
record,
|
|
231
235
|
previous,
|
|
232
236
|
...(scope ? { extra: scope } : {}),
|
|
233
237
|
...(permissions !== undefined ? { permissions } : {}),
|
|
238
|
+
...(systemPermissions !== undefined ? { systemPermissions } : {}),
|
|
234
239
|
});
|
|
235
240
|
// Parse error, type error, unbound identifier, engine fault … — every
|
|
236
241
|
// not-ok verdict resolves to the fallback, but never silently (objectstack-ai/objectstack#5149).
|
|
@@ -13,6 +13,7 @@ export * from './fieldRules.js';
|
|
|
13
13
|
export * from './subjectPermissions.js';
|
|
14
14
|
export * from './rowPredicateCanon.js';
|
|
15
15
|
export * from './listConditional.js';
|
|
16
|
+
export * from './deadlineSettled.js';
|
|
16
17
|
export * from './optionRules.js';
|
|
17
18
|
export * from './optionLint.js';
|
|
18
19
|
export * from './ExpressionCache.js';
|
package/dist/evaluator/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export * from './fieldRules.js';
|
|
|
13
13
|
export * from './subjectPermissions.js';
|
|
14
14
|
export * from './rowPredicateCanon.js';
|
|
15
15
|
export * from './listConditional.js';
|
|
16
|
+
export * from './deadlineSettled.js';
|
|
16
17
|
export * from './optionRules.js';
|
|
17
18
|
export * from './optionLint.js';
|
|
18
19
|
export * from './ExpressionCache.js';
|
|
@@ -78,7 +78,7 @@ export declare function resolveVisibleOptions<T extends OptionLike>(options: rea
|
|
|
78
78
|
* This is deliberately NOT the lenient renderer fallback AGENTS.md #0.1
|
|
79
79
|
* forbids. That rule governs metadata the contract REFUSES ("if the metadata
|
|
80
80
|
* is off-spec, fix it at the producer"); an empty label is metadata the
|
|
81
|
-
* contract ACCEPTS. Measured on the installed `@objectstack/spec`
|
|
81
|
+
* contract ACCEPTS. Measured on the installed `@objectstack/spec` 18.0.0,
|
|
82
82
|
* `SelectOptionSchema`:
|
|
83
83
|
*
|
|
84
84
|
* { value: 'low', label: 'Low' } -> ACCEPT (lit control)
|
|
@@ -57,7 +57,7 @@ export function resolveVisibleOptions(options, record, scope) {
|
|
|
57
57
|
* This is deliberately NOT the lenient renderer fallback AGENTS.md #0.1
|
|
58
58
|
* forbids. That rule governs metadata the contract REFUSES ("if the metadata
|
|
59
59
|
* is off-spec, fix it at the producer"); an empty label is metadata the
|
|
60
|
-
* contract ACCEPTS. Measured on the installed `@objectstack/spec`
|
|
60
|
+
* contract ACCEPTS. Measured on the installed `@objectstack/spec` 18.0.0,
|
|
61
61
|
* `SelectOptionSchema`:
|
|
62
62
|
*
|
|
63
63
|
* { value: 'low', label: 'Low' } -> ACCEPT (lit control)
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
* here, because the engine's contract is that an empty map means "holds
|
|
50
50
|
* nothing" and a caller that has no data must not claim it has that answer.
|
|
51
51
|
*/
|
|
52
|
-
import type { EvalPermissions } from '@objectstack/formula';
|
|
52
|
+
import type { EvalPermissions, EvalSystemPermissions } from '@objectstack/formula';
|
|
53
53
|
/**
|
|
54
54
|
* The key the subject carries its effective object permissions under.
|
|
55
55
|
*
|
|
@@ -74,3 +74,32 @@ export declare function bindSubjectPermissions<T extends object>(subject: T, per
|
|
|
74
74
|
* carries none (see "Absent is not empty" above).
|
|
75
75
|
*/
|
|
76
76
|
export declare function subjectPermissionsOf(subject: unknown): EvalPermissions | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* The key the subject carries its held system capabilities under — the
|
|
79
|
+
* `systemPermissions` list of the `/auth/me/permissions` response, the one
|
|
80
|
+
* input `current_user.hasCapability(name)` is answered from (objectui#13006).
|
|
81
|
+
*
|
|
82
|
+
* It rides beside {@link SUBJECT_PERMISSIONS} for the same reasons: the engine
|
|
83
|
+
* reads it from `EvalContext.systemPermissions`, ⛔ never from a CEL variable,
|
|
84
|
+
* so it can be neither a scope key (every scope key is a root) nor a string
|
|
85
|
+
* member of `current_user` (`current_user.systemPermissions` would be a
|
|
86
|
+
* readable member reaching around the function). A symbol is neither.
|
|
87
|
+
*
|
|
88
|
+
* Absent is not empty, exactly as for the permissions map: a subject carrying
|
|
89
|
+
* no list hands the engine none, and `hasCapability` refuses loudly; an empty
|
|
90
|
+
* list is a real answer ("holds no capability") and evaluates to `false`.
|
|
91
|
+
*/
|
|
92
|
+
export declare const SUBJECT_CAPABILITIES: unique symbol;
|
|
93
|
+
/**
|
|
94
|
+
* The subject to publish as `current_user`, carrying `systemPermissions` when
|
|
95
|
+
* there is a list — the sibling of {@link bindSubjectPermissions}, on the same
|
|
96
|
+
* terms: a shallow copy (the caller's object is never mutated, and every alias
|
|
97
|
+
* must be the returned object), and with no list a subject built from one that
|
|
98
|
+
* had a list is stripped rather than left answering from a stale one.
|
|
99
|
+
*/
|
|
100
|
+
export declare function bindSubjectCapabilities<T extends object>(subject: T, systemPermissions: EvalSystemPermissions | undefined): T;
|
|
101
|
+
/**
|
|
102
|
+
* The held system capabilities a subject carries, or `undefined` when it
|
|
103
|
+
* carries none (absent is not empty — see {@link SUBJECT_CAPABILITIES}).
|
|
104
|
+
*/
|
|
105
|
+
export declare function subjectCapabilitiesOf(subject: unknown): EvalSystemPermissions | undefined;
|
|
@@ -42,3 +42,45 @@ export function subjectPermissionsOf(subject) {
|
|
|
42
42
|
return undefined;
|
|
43
43
|
return subject[SUBJECT_PERMISSIONS];
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The key the subject carries its held system capabilities under — the
|
|
47
|
+
* `systemPermissions` list of the `/auth/me/permissions` response, the one
|
|
48
|
+
* input `current_user.hasCapability(name)` is answered from (objectui#13006).
|
|
49
|
+
*
|
|
50
|
+
* It rides beside {@link SUBJECT_PERMISSIONS} for the same reasons: the engine
|
|
51
|
+
* reads it from `EvalContext.systemPermissions`, ⛔ never from a CEL variable,
|
|
52
|
+
* so it can be neither a scope key (every scope key is a root) nor a string
|
|
53
|
+
* member of `current_user` (`current_user.systemPermissions` would be a
|
|
54
|
+
* readable member reaching around the function). A symbol is neither.
|
|
55
|
+
*
|
|
56
|
+
* Absent is not empty, exactly as for the permissions map: a subject carrying
|
|
57
|
+
* no list hands the engine none, and `hasCapability` refuses loudly; an empty
|
|
58
|
+
* list is a real answer ("holds no capability") and evaluates to `false`.
|
|
59
|
+
*/
|
|
60
|
+
export const SUBJECT_CAPABILITIES = Symbol.for('@object-ui/core:subject-capabilities');
|
|
61
|
+
/**
|
|
62
|
+
* The subject to publish as `current_user`, carrying `systemPermissions` when
|
|
63
|
+
* there is a list — the sibling of {@link bindSubjectPermissions}, on the same
|
|
64
|
+
* terms: a shallow copy (the caller's object is never mutated, and every alias
|
|
65
|
+
* must be the returned object), and with no list a subject built from one that
|
|
66
|
+
* had a list is stripped rather than left answering from a stale one.
|
|
67
|
+
*/
|
|
68
|
+
export function bindSubjectCapabilities(subject, systemPermissions) {
|
|
69
|
+
if (systemPermissions === undefined) {
|
|
70
|
+
if (!Object.prototype.hasOwnProperty.call(subject, SUBJECT_CAPABILITIES))
|
|
71
|
+
return subject;
|
|
72
|
+
const stripped = { ...subject };
|
|
73
|
+
delete stripped[SUBJECT_CAPABILITIES];
|
|
74
|
+
return stripped;
|
|
75
|
+
}
|
|
76
|
+
return { ...subject, [SUBJECT_CAPABILITIES]: systemPermissions };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The held system capabilities a subject carries, or `undefined` when it
|
|
80
|
+
* carries none (absent is not empty — see {@link SUBJECT_CAPABILITIES}).
|
|
81
|
+
*/
|
|
82
|
+
export function subjectCapabilitiesOf(subject) {
|
|
83
|
+
if (subject === null || typeof subject !== 'object')
|
|
84
|
+
return undefined;
|
|
85
|
+
return subject[SUBJECT_CAPABILITIES];
|
|
86
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export * from './builder/schema-builder.js';
|
|
|
17
17
|
export * from './utils/dom-props.js';
|
|
18
18
|
export * from './utils/filter-converter.js';
|
|
19
19
|
export * from './utils/managedBy.js';
|
|
20
|
+
export * from './utils/affordanceGrants.js';
|
|
20
21
|
export * from './utils/extract-records.js';
|
|
21
22
|
export * from './utils/non-grid-row-ceiling.js';
|
|
22
23
|
export * from './utils/emptiness.js';
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,9 @@ export * from './builder/schema-builder.js';
|
|
|
21
21
|
export * from './utils/dom-props.js';
|
|
22
22
|
export * from './utils/filter-converter.js';
|
|
23
23
|
export * from './utils/managedBy.js';
|
|
24
|
+
// The affordance-to-grant map (objectui#12082): every console affordance that
|
|
25
|
+
// offers a write reads the grant it exercises from this one table.
|
|
26
|
+
export * from './utils/affordanceGrants.js';
|
|
24
27
|
export * from './utils/extract-records.js';
|
|
25
28
|
// The non-grid row ceiling (objectui#7210), homed beside the `extractRecords`
|
|
26
29
|
// it wraps by objectui#7508's ruling A′. `@object-ui/react` re-exports three of
|
|
@@ -136,7 +136,7 @@ export type ComponentDeprecation = {
|
|
|
136
136
|
* publishing registry mechanics on the general type was the alternative
|
|
137
137
|
* objectui#6067 weighed and rejected.
|
|
138
138
|
*
|
|
139
|
-
* They live in their OWN named type so that `
|
|
139
|
+
* They live in their OWN named type so that `RegistryComponentMeta` below can be
|
|
140
140
|
* DERIVED from the canonical declaration instead of restating it. Until
|
|
141
141
|
* objectui#6067 this file carried a second, thirteen-key structural copy of
|
|
142
142
|
* the name: the nine shared members restated from `@object-ui/types`' `base.ts`,
|
|
@@ -238,6 +238,18 @@ export type RegistryComponentMetaExtras = {
|
|
|
238
238
|
* the two keys the divergence had made unwritable at the very declaration most
|
|
239
239
|
* component registrations import.
|
|
240
240
|
*
|
|
241
|
+
* ## Its own NAME since objectui#6349 (batch 4)
|
|
242
|
+
*
|
|
243
|
+
* This type used to be published as `ComponentMeta` too — the same name as the
|
|
244
|
+
* canonical declaration it extends, with a different meaning (registration
|
|
245
|
+
* metadata: the canonical members PLUS the five registry-only keys). A derived
|
|
246
|
+
* declaration is still a second authority for the name, so the card's rename
|
|
247
|
+
* branch applies: the registration type is `RegistryComponentMeta` (the name
|
|
248
|
+
* `PluginScopeImpl.ts` already gave it locally), and `ComponentMeta` from this
|
|
249
|
+
* package is a plain re-export of `@object-ui/types`' one (just below). The
|
|
250
|
+
* registry's own doors — `register`, `registerLazy`, `getMeta` — are typed
|
|
251
|
+
* with this name, so a registration literal keeps accepting the registry keys.
|
|
252
|
+
*
|
|
241
253
|
* NOTE for anyone pinning this: every member of both halves is OPTIONAL, so
|
|
242
254
|
* `extends` is mutually TRUE between the two shapes even when their key sets
|
|
243
255
|
* differ. Measured on the EMITTED `.d.ts` of both packages immediately before
|
|
@@ -248,7 +260,15 @@ export type RegistryComponentMetaExtras = {
|
|
|
248
260
|
* compares `keyof` sets instead and keeps the assignability check beside it as
|
|
249
261
|
* the labelled control that shows why.
|
|
250
262
|
*/
|
|
251
|
-
export type
|
|
263
|
+
export type RegistryComponentMeta = CanonicalComponentMeta & RegistryComponentMetaExtras;
|
|
264
|
+
/**
|
|
265
|
+
* The general component metadata — `@object-ui/types`' ONE declaration,
|
|
266
|
+
* re-exported so an import of `ComponentMeta` from this package resolves to
|
|
267
|
+
* the same type as one from `@object-ui/types` (objectui#6349, batch 4). It
|
|
268
|
+
* carries none of the registry-only keys; a registration is typed with
|
|
269
|
+
* {@link RegistryComponentMeta}.
|
|
270
|
+
*/
|
|
271
|
+
export type { ComponentMeta } from '@object-ui/types';
|
|
252
272
|
/**
|
|
253
273
|
* ONE authority for `ComponentConfig` (objectui#6298) — this package RE-EXPORTS
|
|
254
274
|
* `@object-ui/types`' declaration instead of declaring a second one, the same
|
|
@@ -280,8 +300,9 @@ export type ComponentMeta = CanonicalComponentMeta & RegistryComponentMetaExtras
|
|
|
280
300
|
* counts declarations and ALIASING re-exports, never `export type { X } from …`
|
|
281
301
|
* — which is why this convergence takes `ComponentConfig` off that gate's
|
|
282
302
|
* `KNOWN_COLLISIONS` baseline. Deriving a new declaration here instead would
|
|
283
|
-
* NOT have: `ComponentMeta` was converged that way by PR #6297 and
|
|
284
|
-
*
|
|
303
|
+
* NOT have: `ComponentMeta` was converged that way by PR #6297 and stayed a row
|
|
304
|
+
* on that baseline until objectui#6349 gave the registry's declaration its own
|
|
305
|
+
* name, `RegistryComponentMeta`.
|
|
285
306
|
*/
|
|
286
307
|
export type { ComponentConfig } from '@object-ui/types';
|
|
287
308
|
/**
|
|
@@ -294,7 +315,7 @@ export type { ComponentConfig } from '@object-ui/types';
|
|
|
294
315
|
* one declaration for the shared members ({@link ComponentConfig}, in
|
|
295
316
|
* `@object-ui/types`), a named extension for the rest
|
|
296
317
|
* ({@link RegistryComponentMetaExtras}) — which is the shape PR #6297 gave
|
|
297
|
-
* {@link
|
|
318
|
+
* {@link RegistryComponentMeta}.
|
|
298
319
|
*
|
|
299
320
|
* It exists because the extras are NOT optional decoration on a registry entry:
|
|
300
321
|
* {@link Registry.getNamespaceComponents} filters on `config.namespace`, and
|
|
@@ -302,7 +323,7 @@ export type { ComponentConfig } from '@object-ui/types';
|
|
|
302
323
|
* bare re-export as the entry type would have silently dropped them.
|
|
303
324
|
*
|
|
304
325
|
* ⚠️ `ComponentConfig` remains the AUTHORING vocabulary and the general name;
|
|
305
|
-
* registrations are checked against {@link
|
|
326
|
+
* registrations are checked against {@link RegistryComponentMeta}, never against this.
|
|
306
327
|
* Nothing writes a `RegistryComponentConfig` literal — the registry builds them.
|
|
307
328
|
*/
|
|
308
329
|
export type RegistryComponentConfig<T = any> = ComponentConfig<T> & RegistryComponentMetaExtras;
|
|
@@ -322,7 +343,7 @@ export type RegistryComponentConfig<T = any> = ComponentConfig<T> & RegistryComp
|
|
|
322
343
|
* — the read stamps it, and `manifestFromConfigs` carries it into the manifest
|
|
323
344
|
* so a consumer that means the CURATED vocabulary can filter it out.
|
|
324
345
|
*/
|
|
325
|
-
export type PublicComponentConfig<T = any> = Omit<
|
|
346
|
+
export type PublicComponentConfig<T = any> = Omit<RegistryComponentMeta, 'tier'> & {
|
|
326
347
|
type: string;
|
|
327
348
|
/**
|
|
328
349
|
* The registration's declared tier, or `'html'` for an entry that reaches the
|
|
@@ -330,7 +351,7 @@ export type PublicComponentConfig<T = any> = Omit<ComponentMeta, 'tier'> & {
|
|
|
330
351
|
* curated roster or a `tier: 'public'` opt-in. Absent or `'public'` = the
|
|
331
352
|
* curated JSON-surface vocabulary.
|
|
332
353
|
*/
|
|
333
|
-
tier?:
|
|
354
|
+
tier?: RegistryComponentMeta['tier'] | 'html';
|
|
334
355
|
component?: ComponentRenderer<T>;
|
|
335
356
|
/** True while this entry is a `registerLazy` stub whose loader has not run. */
|
|
336
357
|
lazy?: boolean;
|
|
@@ -380,7 +401,7 @@ export type LazyComponentLoader = () => Promise<unknown>;
|
|
|
380
401
|
* The spliced element is an `InjectedComponentInput` (`@object-ui/types`): a
|
|
381
402
|
* `ComponentInput` plus the framework-set `binding` marker, so it is a plain
|
|
382
403
|
* subtype of the array's element type and the return value type-checks as a
|
|
383
|
-
* `
|
|
404
|
+
* `RegistryComponentMeta` with no assertion. This used to read `as ComponentMeta`,
|
|
384
405
|
* because `ELEMENT_DATA_SOURCE_INPUT` carried a hand-written inline type and
|
|
385
406
|
* `binding` had no declared home — and a cast at the one place the key is
|
|
386
407
|
* written is exactly what would have hidden any later drift between the
|
|
@@ -394,7 +415,7 @@ export type LazyComponentLoader = () => Promise<unknown>;
|
|
|
394
415
|
* pins the positive half, that the local below is ANNOTATED. Keep it typed: if
|
|
395
416
|
* the shape ever stops fitting, that line is where the compiler should say so.
|
|
396
417
|
*/
|
|
397
|
-
export declare function withElementDataSourceInput<T>(component: ComponentRenderer<T>, meta?:
|
|
418
|
+
export declare function withElementDataSourceInput<T>(component: ComponentRenderer<T>, meta?: RegistryComponentMeta): RegistryComponentMeta | undefined;
|
|
398
419
|
export declare class Registry<T = any> {
|
|
399
420
|
private components;
|
|
400
421
|
private lazyEntries;
|
|
@@ -430,7 +451,7 @@ export declare class Registry<T = any> {
|
|
|
430
451
|
* registry.register('button', ButtonComponent);
|
|
431
452
|
* // Accessible as 'button'
|
|
432
453
|
*/
|
|
433
|
-
register(type: string, component: ComponentRenderer<T>, meta?:
|
|
454
|
+
register(type: string, component: ComponentRenderer<T>, meta?: RegistryComponentMeta): void;
|
|
434
455
|
/**
|
|
435
456
|
* Remove a previously registered component. Mirrors {@link register} by
|
|
436
457
|
* clearing both the namespaced key and the bare-name fallback (when the
|
|
@@ -473,7 +494,7 @@ export declare class Registry<T = any> {
|
|
|
473
494
|
* @example
|
|
474
495
|
* ComponentRegistry.registerLazy('object-map', () => import('@object-ui/plugin-map'), { namespace: 'plugin-map' });
|
|
475
496
|
*/
|
|
476
|
-
registerLazy(type: string, loader: LazyComponentLoader, meta?:
|
|
497
|
+
registerLazy(type: string, loader: LazyComponentLoader, meta?: RegistryComponentMeta): void;
|
|
477
498
|
/**
|
|
478
499
|
* Returns true if `type` (or its namespaced form) has a registered lazy
|
|
479
500
|
* loader awaiting first use.
|
|
@@ -575,7 +596,7 @@ export declare class Registry<T = any> {
|
|
|
575
596
|
* absent until the chunk loads. Consumers should treat that as "not yet
|
|
576
597
|
* known", not as "declares no props".
|
|
577
598
|
*/
|
|
578
|
-
getMeta(type: string, namespace?: string):
|
|
599
|
+
getMeta(type: string, namespace?: string): RegistryComponentMeta | undefined;
|
|
579
600
|
/**
|
|
580
601
|
* The deprecation `type` declares FOR `surface` — or `undefined` when it
|
|
581
602
|
* declares none there (objectui#6674).
|
|
@@ -8,18 +8,6 @@
|
|
|
8
8
|
import { ELEMENT_DATA_SOURCE_INPUT, isElementDataSourceBlock, } from '../data-scope/element-data-source.js';
|
|
9
9
|
import { PUBLIC_BLOCKS } from './public-blocks.js';
|
|
10
10
|
import { HTML_TIER_INTRINSICS } from './html-tier-intrinsics.js';
|
|
11
|
-
/**
|
|
12
|
-
* The full type a pending lazy stub DECLARES for a bare key.
|
|
13
|
-
*
|
|
14
|
-
* `registerLazy` computes `namespace:type` and stores the stub under both that
|
|
15
|
-
* key and the bare one; nothing on the entry itself records which bare key it
|
|
16
|
-
* claims, so the claim has to be recomputed from the entry's own meta. Spelled
|
|
17
|
-
* once here because both doors' collision guards need it and a second copy
|
|
18
|
-
* would be a second thing to keep in step with `registerLazy`'s own line.
|
|
19
|
-
*/
|
|
20
|
-
function lazyStubFullType(type, entry) {
|
|
21
|
-
return entry.meta?.namespace ? `${entry.meta.namespace}:${type}` : type;
|
|
22
|
-
}
|
|
23
11
|
/**
|
|
24
12
|
* Emit the spec's `dataSource` input for a registration whose renderer wraps
|
|
25
13
|
* `ElementDataSourceGate` (objectui#6678).
|
|
@@ -57,7 +45,7 @@ function lazyStubFullType(type, entry) {
|
|
|
57
45
|
* The spliced element is an `InjectedComponentInput` (`@object-ui/types`): a
|
|
58
46
|
* `ComponentInput` plus the framework-set `binding` marker, so it is a plain
|
|
59
47
|
* subtype of the array's element type and the return value type-checks as a
|
|
60
|
-
* `
|
|
48
|
+
* `RegistryComponentMeta` with no assertion. This used to read `as ComponentMeta`,
|
|
61
49
|
* because `ELEMENT_DATA_SOURCE_INPUT` carried a hand-written inline type and
|
|
62
50
|
* `binding` had no declared home — and a cast at the one place the key is
|
|
63
51
|
* written is exactly what would have hidden any later drift between the
|
|
@@ -204,8 +192,7 @@ export class Registry {
|
|
|
204
192
|
// owner and the opt-out settles this contest exactly as it does on the
|
|
205
193
|
// lazy door. Both doors therefore prescribe the same thing now, which
|
|
206
194
|
// `each door prescribes the remedy that is true for it` pins.
|
|
207
|
-
const
|
|
208
|
-
const stubType = stub ? lazyStubFullType(type, stub) : undefined;
|
|
195
|
+
const stubType = this.lazyEntries.get(type)?.fullType;
|
|
209
196
|
if (stubType && stubType !== fullType) {
|
|
210
197
|
console.warn(`Component "${type}" bare-name fallback is being overwritten by "${fullType}", ` +
|
|
211
198
|
`which a pending lazy stub already claims for "${stubType}". Which one an ` +
|
|
@@ -283,7 +270,7 @@ export class Registry {
|
|
|
283
270
|
// The same ownership test, on the other table.
|
|
284
271
|
if (namespace) {
|
|
285
272
|
const bareStub = this.lazyEntries.get(type);
|
|
286
|
-
if (bareStub &&
|
|
273
|
+
if (bareStub && bareStub.fullType === fullType) {
|
|
287
274
|
this.lazyEntries.delete(type);
|
|
288
275
|
}
|
|
289
276
|
}
|
|
@@ -305,7 +292,7 @@ export class Registry {
|
|
|
305
292
|
*/
|
|
306
293
|
registerLazy(type, loader, meta) {
|
|
307
294
|
const fullType = meta?.namespace ? `${meta.namespace}:${type}` : type;
|
|
308
|
-
const entry = { loader, meta };
|
|
295
|
+
const entry = { loader, meta, fullType };
|
|
309
296
|
this.lazyEntries.set(fullType, entry);
|
|
310
297
|
if (meta?.namespace && !meta?.skipFallback) {
|
|
311
298
|
// Collision guard (objectui#9821). This door took the same bare-name
|
|
@@ -335,11 +322,7 @@ export class Registry {
|
|
|
335
322
|
// genuine contest.
|
|
336
323
|
const loaded = this.components.get(type);
|
|
337
324
|
const priorStub = loaded ? undefined : this.lazyEntries.get(type);
|
|
338
|
-
const claimedBy = loaded
|
|
339
|
-
? loaded.type
|
|
340
|
-
: priorStub
|
|
341
|
-
? lazyStubFullType(type, priorStub)
|
|
342
|
-
: undefined;
|
|
325
|
+
const claimedBy = loaded ? loaded.type : priorStub?.fullType;
|
|
343
326
|
if (claimedBy && claimedBy !== fullType) {
|
|
344
327
|
console.warn(`Lazy component "${type}" bare-name fallback is being overwritten by "${fullType}", ` +
|
|
345
328
|
`which ${loaded ? 'a loaded registration' : 'another pending stub'} already claims ` +
|