@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.
Files changed (52) hide show
  1. package/CHANGELOG.md +650 -0
  2. package/README.md +127 -0
  3. package/dist/actions/ActionRunner.d.ts +192 -49
  4. package/dist/actions/ActionRunner.js +389 -102
  5. package/dist/actions/UndoManager.d.ts +8 -13
  6. package/dist/actions/actionKeys.d.ts +1 -1
  7. package/dist/actions/actionKeys.js +10 -0
  8. package/dist/actions/index.d.ts +1 -0
  9. package/dist/actions/index.js +1 -0
  10. package/dist/actions/navigationTarget.d.ts +41 -0
  11. package/dist/actions/navigationTarget.js +20 -0
  12. package/dist/actions/recordDelete.d.ts +59 -23
  13. package/dist/actions/recordDelete.js +40 -42
  14. package/dist/adapters/ApiDataSource.d.ts +8 -4
  15. package/dist/adapters/ApiDataSource.js +19 -7
  16. package/dist/adapters/resolveDataSource.d.ts +9 -6
  17. package/dist/adapters/resolveDataSource.js +9 -12
  18. package/dist/data-scope/ViewDataProvider.d.ts +7 -24
  19. package/dist/data-scope/ViewDataProvider.js +6 -53
  20. package/dist/evaluator/deadlineSettled.d.ts +40 -0
  21. package/dist/evaluator/deadlineSettled.js +45 -0
  22. package/dist/evaluator/fieldRules.js +6 -1
  23. package/dist/evaluator/index.d.ts +1 -0
  24. package/dist/evaluator/index.js +1 -0
  25. package/dist/evaluator/optionRules.d.ts +1 -1
  26. package/dist/evaluator/optionRules.js +1 -1
  27. package/dist/evaluator/subjectPermissions.d.ts +30 -1
  28. package/dist/evaluator/subjectPermissions.js +42 -0
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.js +3 -0
  31. package/dist/registry/Registry.d.ts +34 -13
  32. package/dist/registry/Registry.js +5 -22
  33. package/dist/utils/affordanceGrants.d.ts +371 -0
  34. package/dist/utils/affordanceGrants.js +252 -0
  35. package/dist/utils/chart-series.d.ts +34 -0
  36. package/dist/utils/chart-series.js +25 -2
  37. package/dist/utils/date-display.d.ts +42 -50
  38. package/dist/utils/date-display.js +192 -11
  39. package/dist/utils/expand-fields.d.ts +6 -4
  40. package/dist/utils/expand-fields.js +6 -4
  41. package/dist/utils/filter-converter.d.ts +6 -2
  42. package/dist/utils/filter-converter.js +6 -2
  43. package/dist/utils/normalize-list-view.d.ts +16 -12
  44. package/dist/utils/normalize-list-view.js +66 -67
  45. package/dist/utils/predicate-fields.js +6 -2
  46. package/dist/utils/record-source.d.ts +1 -1
  47. package/dist/utils/record-source.js +1 -1
  48. package/dist/utils/reference-keys.js +1 -1
  49. package/dist/validation/schema-validator.js +29 -5
  50. package/dist/validation/server-owned-value.d.ts +5 -1
  51. package/dist/validation/server-owned-value.js +5 -1
  52. 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 three data modes:
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. 'api' — Custom REST API endpoints (read/write URLs)
16
- * 3. 'value' — Static data (pre-loaded items array)
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';
@@ -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` 17.6.0,
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` 17.6.0,
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 `ComponentMeta` below can be
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 ComponentMeta = CanonicalComponentMeta & RegistryComponentMetaExtras;
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 is still a
284
- * row on that baseline today.
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 ComponentMeta}.
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 ComponentMeta}, never against this.
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<ComponentMeta, 'tier'> & {
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?: ComponentMeta['tier'] | 'html';
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
- * `ComponentMeta` with no assertion. This used to read `as ComponentMeta`,
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?: ComponentMeta): ComponentMeta | undefined;
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?: ComponentMeta): void;
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?: ComponentMeta): void;
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): ComponentMeta | undefined;
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
- * `ComponentMeta` with no assertion. This used to read `as ComponentMeta`,
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 stub = this.lazyEntries.get(type);
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 && lazyStubFullType(type, bareStub) === fullType) {
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 ` +