@object-ui/react 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.
Files changed (103) hide show
  1. package/CHANGELOG.md +4100 -0
  2. package/README.md +303 -46
  3. package/dist/LazyPluginLoader.d.ts +14 -7
  4. package/dist/LazyPluginLoader.js +14 -7
  5. package/dist/SchemaRenderer.d.ts +38 -7
  6. package/dist/SchemaRenderer.js +1603 -60
  7. package/dist/context/ActionContext.js +40 -1
  8. package/dist/context/DiscussionContext.d.ts +12 -0
  9. package/dist/context/DiscussionContext.js +2 -0
  10. package/dist/context/DrillNavigationContext.d.ts +13 -0
  11. package/dist/context/HostNavigationContext.d.ts +82 -0
  12. package/dist/context/HostNavigationContext.js +65 -0
  13. package/dist/context/InlineEditContext.d.ts +81 -1
  14. package/dist/context/InlineEditContext.js +8 -2
  15. package/dist/context/RecordContext.d.ts +48 -5
  16. package/dist/context/RecordContext.js +10 -1
  17. package/dist/context/RelatedRecordActionsContext.d.ts +20 -1
  18. package/dist/context/SchemaRendererContext.d.ts +54 -2
  19. package/dist/context/SchemaRendererContext.js +38 -5
  20. package/dist/context/ThemeContext.js +1 -1
  21. package/dist/context/actionRunnerTranslator.d.ts +27 -0
  22. package/dist/context/actionRunnerTranslator.js +37 -0
  23. package/dist/context/index.d.ts +13 -12
  24. package/dist/context/index.js +13 -12
  25. package/dist/element-data-source/ElementDataSourceGate.d.ts +163 -3
  26. package/dist/element-data-source/ElementDataSourceGate.js +407 -15
  27. package/dist/hooks/index.d.ts +37 -35
  28. package/dist/hooks/index.js +44 -35
  29. package/dist/hooks/useActionEngine.js +47 -4
  30. package/dist/hooks/useActionRunner.js +4 -0
  31. package/dist/hooks/useActionTextLocalizer.d.ts +21 -8
  32. package/dist/hooks/useActionTextLocalizer.js +33 -8
  33. package/dist/hooks/useCapabilityGate.js +6 -5
  34. package/dist/hooks/useConfigBagEvaluator.d.ts +30 -0
  35. package/dist/hooks/useConfigBagEvaluator.js +39 -0
  36. package/dist/hooks/useCrudShortcuts.js +1 -1
  37. package/dist/hooks/useDatasetDimensionLabels.js +1 -1
  38. package/dist/hooks/useDensityMode.d.ts +6 -2
  39. package/dist/hooks/useDensityMode.js +6 -2
  40. package/dist/hooks/useDiscovery.d.ts +1 -10
  41. package/dist/hooks/useDiscovery.js +1 -1
  42. package/dist/hooks/useETagCache.js +25 -4
  43. package/dist/hooks/useElementDataSource.d.ts +15 -2
  44. package/dist/hooks/useElementDataSource.js +29 -9
  45. package/dist/hooks/useExpression.d.ts +52 -38
  46. package/dist/hooks/useExpression.js +62 -42
  47. package/dist/hooks/useFilterScope.d.ts +22 -2
  48. package/dist/hooks/useFilterScope.js +29 -4
  49. package/dist/hooks/useGlobalUndo.d.ts +10 -0
  50. package/dist/hooks/useGlobalUndo.js +112 -46
  51. package/dist/hooks/useNavigationOverlay.d.ts +77 -12
  52. package/dist/hooks/useNavigationOverlay.js +119 -9
  53. package/dist/hooks/useOffline.js +65 -11
  54. package/dist/hooks/usePageAssignment.d.ts +8 -5
  55. package/dist/hooks/usePageAssignment.js +38 -17
  56. package/dist/hooks/usePageTransition.js +1 -1
  57. package/dist/hooks/usePageVariableActionBridge.js +2 -2
  58. package/dist/hooks/useRecordSearch.d.ts +34 -1
  59. package/dist/hooks/useRecordSearch.js +42 -7
  60. package/dist/hooks/useResolvedFilter.d.ts +46 -0
  61. package/dist/hooks/useResolvedFilter.js +130 -0
  62. package/dist/hooks/useSchemaPersistence.d.ts +7 -0
  63. package/dist/hooks/useSchemaPersistence.js +144 -5
  64. package/dist/hooks/useSettledSchema.d.ts +123 -0
  65. package/dist/hooks/useSettledSchema.js +186 -0
  66. package/dist/hooks/useViewData.d.ts +7 -2
  67. package/dist/hooks/useViewData.js +1 -1
  68. package/dist/hooks/useViewTransition.js +1 -1
  69. package/dist/index.d.ts +18 -11
  70. package/dist/index.js +54 -10
  71. package/dist/schema-input.d.ts +55 -14
  72. package/dist/schema-input.js +55 -12
  73. package/dist/utils/configBag.d.ts +68 -0
  74. package/dist/utils/configBag.js +70 -0
  75. package/dist/utils/error-message.d.ts +59 -0
  76. package/dist/utils/error-message.js +116 -1
  77. package/dist/utils/inlineAria.d.ts +61 -0
  78. package/dist/utils/inlineAria.js +70 -0
  79. package/dist/utils/nonGridRowCeiling.d.ts +44 -0
  80. package/dist/utils/nonGridRowCeiling.js +78 -0
  81. package/dist/utils/paramsBag.d.ts +28 -0
  82. package/dist/utils/paramsBag.js +146 -0
  83. package/dist/utils/propsBagDiagnostic.d.ts +196 -0
  84. package/dist/utils/propsBagDiagnostic.js +421 -0
  85. package/dist/utils/refusedDataPropDiagnostic.d.ts +44 -0
  86. package/dist/utils/refusedDataPropDiagnostic.js +94 -0
  87. package/dist/utils/unevaluatedExpression.d.ts +110 -0
  88. package/dist/utils/unevaluatedExpression.js +257 -0
  89. package/dist/utils/visibilityDiagnostic.d.ts +371 -0
  90. package/dist/utils/visibilityDiagnostic.js +551 -0
  91. package/package.json +10 -10
  92. package/dist/hooks/useClientNotifications.d.ts +0 -47
  93. package/dist/hooks/useClientNotifications.js +0 -162
  94. package/dist/spec-bridge/SpecBridge.d.ts +0 -28
  95. package/dist/spec-bridge/SpecBridge.js +0 -56
  96. package/dist/spec-bridge/bridges/form-view.d.ts +0 -82
  97. package/dist/spec-bridge/bridges/form-view.js +0 -126
  98. package/dist/spec-bridge/bridges/list-view.d.ts +0 -20
  99. package/dist/spec-bridge/bridges/list-view.js +0 -241
  100. package/dist/spec-bridge/index.d.ts +0 -11
  101. package/dist/spec-bridge/index.js +0 -10
  102. package/dist/spec-bridge/types.d.ts +0 -55
  103. package/dist/spec-bridge/types.js +0 -8
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createContext, useContext, useMemo } from 'react';
3
+ import { usePredicateScope } from '../hooks/useExpression.js';
3
4
  const SchemaRendererContext = createContext(null);
4
5
  export { SchemaRendererContext };
5
6
  export const SchemaRendererProvider = ({ children, dataSource, debug, debugFlags, apiFetch, }) => {
@@ -18,13 +19,45 @@ export const useSchemaContext = () => {
18
19
  }
19
20
  return context;
20
21
  };
22
+ /**
23
+ * Resolve a `bind` path against the ambient predicate scope.
24
+ *
25
+ * ## What it reads, and what it deliberately does not (objectui#9308)
26
+ *
27
+ * The scope a host publishes through `PredicateScopeProvider` — the same
28
+ * channel `useCondition` / `useExpression` merge under a locally-passed
29
+ * context, and the one app-shell's `ExpressionProvider` already feeds. It is
30
+ * NOT `SchemaRendererContext.dataSource`.
31
+ *
32
+ * `dataSource` is the host's injected ADAPTER, declared as the published
33
+ * `DataSource` contract (objectui#7912). An adapter has no `users` member, no
34
+ * `value` member, no member a `bind` path names — so walking it resolved
35
+ * `undefined` for every conformant host, and the nine production readers of
36
+ * this hook have all been running their fallback (`boundData || schema.items`,
37
+ * `|| schema.nodes`, or a fall-through to their own fetch chain) ever since.
38
+ * The only hosts it answered were ones injecting a data bag through a key the
39
+ * contract says is an adapter, which has been a compile error since
40
+ * objectui#7912.
41
+ *
42
+ * Maintainer ruling 2026-09-13 (option B) points it at the channel that can
43
+ * actually answer it. No published key is added: the provider, the hook and
44
+ * the host wiring all already exist.
45
+ *
46
+ * Returns `undefined` for an absent or empty path, and for any path the scope
47
+ * does not carry — the readers' fallbacks are what run then, exactly as
48
+ * before.
49
+ */
21
50
  export const useDataScope = (path) => {
22
- const context = useContext(SchemaRendererContext);
23
- const dataSource = context?.dataSource;
51
+ const scope = usePredicateScope();
24
52
  if (!path)
25
53
  return undefined;
26
- if (!dataSource)
54
+ if (!scope)
27
55
  return undefined;
28
- // Simple path resolution for now. In real app might be more complex
29
- return path.split('.').reduce((acc, part) => acc && acc[part], dataSource);
56
+ // Simple path resolution for now. In real app might be more complex.
57
+ //
58
+ // The accumulator is `any` BY DECLARATION: this walk addresses arbitrary
59
+ // member names on a host-published bag, and `Record<string, any>` declares
60
+ // none of them beyond the first segment. The hook's published return type is
61
+ // unchanged — `any` before and after — so no reader of `useDataScope` moves.
62
+ return path.split('.').reduce((acc, part) => acc && acc[part], scope);
30
63
  };
@@ -131,7 +131,7 @@ export function ThemeProvider({ themes: themesInput = [], defaultTheme, defaultM
131
131
  // Apply mode class
132
132
  el.classList.remove('light', 'dark');
133
133
  el.classList.add(resolvedMode);
134
- // No favicon here: spec v17 (#3494) pruned `Theme.logo`, so the key is
134
+ // No favicon here: spec v17 (objectstack-ai/objectstack#3494) pruned `Theme.logo`, so the key is
135
135
  // stripped at parse and could never arrive. The live favicon comes from
136
136
  // operator branding (`getFaviconUrl()`), applied in the console's
137
137
  // index.html, main.tsx, and on route change.
@@ -0,0 +1,27 @@
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
+ /** What `ActionRunner.setTranslator` takes. */
9
+ type ActionRunnerTranslate = (key: string, options: {
10
+ defaultValue: string;
11
+ }) => string;
12
+ /**
13
+ * The translator both React owners of an `ActionRunner` — `<ActionProvider>`
14
+ * and `useActionRunner` — install on the runner they build, so the text the
15
+ * runner writes itself (the generic success toast an action with no
16
+ * `successMessage` ends in) renders in the session's language instead of
17
+ * English. Internal to this package; not re-exported from its entry.
18
+ *
19
+ * The function is created once per mount and reads the latest `t` when it is
20
+ * CALLED, so a language switch reaches a runner that was built before it. Both
21
+ * owners rebuild their runner only when their `context` changes, and a
22
+ * translator captured at build time would keep answering in the language that
23
+ * was active then. The identity comes from `useState`'s initializer, not from a
24
+ * memo, so nothing depends on a `useMemo` result staying put (AGENTS.md #10).
25
+ */
26
+ export declare function useActionRunnerTranslator(): ActionRunnerTranslate;
27
+ export {};
@@ -0,0 +1,37 @@
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
+ import { useEffect, useRef, useState } from 'react';
9
+ import { useObjectTranslation } from '@object-ui/i18n';
10
+ /**
11
+ * The translator both React owners of an `ActionRunner` — `<ActionProvider>`
12
+ * and `useActionRunner` — install on the runner they build, so the text the
13
+ * runner writes itself (the generic success toast an action with no
14
+ * `successMessage` ends in) renders in the session's language instead of
15
+ * English. Internal to this package; not re-exported from its entry.
16
+ *
17
+ * The function is created once per mount and reads the latest `t` when it is
18
+ * CALLED, so a language switch reaches a runner that was built before it. Both
19
+ * owners rebuild their runner only when their `context` changes, and a
20
+ * translator captured at build time would keep answering in the language that
21
+ * was active then. The identity comes from `useState`'s initializer, not from a
22
+ * memo, so nothing depends on a `useMemo` result staying put (AGENTS.md #10).
23
+ */
24
+ export function useActionRunnerTranslator() {
25
+ const { t } = useObjectTranslation();
26
+ const latest = useRef(t);
27
+ // No dependency list: after every render, the ref holds that render's `t`.
28
+ // Actions run from user events, which arrive after the commit.
29
+ useEffect(() => {
30
+ latest.current = t;
31
+ });
32
+ const [translate] = useState(() => {
33
+ const read = (key, options) => String(latest.current(key, options));
34
+ return read;
35
+ });
36
+ return translate;
37
+ }
@@ -1,12 +1,13 @@
1
- export * from './SchemaRendererContext';
2
- export * from './PreviewModeContext';
3
- export * from './ActionContext';
4
- export * from './ThemeContext';
5
- export * from './NotificationContext';
6
- export * from './DndContext';
7
- export * from './AppShellContext';
8
- export * from './RecordContext';
9
- export * from './InlineEditContext';
10
- export * from './DiscussionContext';
11
- export * from './DrillNavigationContext';
12
- export * from './RelatedRecordActionsContext';
1
+ export * from './SchemaRendererContext.js';
2
+ export * from './PreviewModeContext.js';
3
+ export * from './ActionContext.js';
4
+ export * from './ThemeContext.js';
5
+ export * from './NotificationContext.js';
6
+ export * from './DndContext.js';
7
+ export * from './AppShellContext.js';
8
+ export * from './RecordContext.js';
9
+ export * from './InlineEditContext.js';
10
+ export * from './DiscussionContext.js';
11
+ export * from './DrillNavigationContext.js';
12
+ export * from './HostNavigationContext.js';
13
+ export * from './RelatedRecordActionsContext.js';
@@ -1,12 +1,13 @@
1
- export * from './SchemaRendererContext';
2
- export * from './PreviewModeContext';
3
- export * from './ActionContext';
4
- export * from './ThemeContext';
5
- export * from './NotificationContext';
6
- export * from './DndContext';
7
- export * from './AppShellContext';
8
- export * from './RecordContext';
9
- export * from './InlineEditContext';
10
- export * from './DiscussionContext';
11
- export * from './DrillNavigationContext';
12
- export * from './RelatedRecordActionsContext';
1
+ export * from './SchemaRendererContext.js';
2
+ export * from './PreviewModeContext.js';
3
+ export * from './ActionContext.js';
4
+ export * from './ThemeContext.js';
5
+ export * from './NotificationContext.js';
6
+ export * from './DndContext.js';
7
+ export * from './AppShellContext.js';
8
+ export * from './RecordContext.js';
9
+ export * from './InlineEditContext.js';
10
+ export * from './DiscussionContext.js';
11
+ export * from './DrillNavigationContext.js';
12
+ export * from './HostNavigationContext.js';
13
+ export * from './RelatedRecordActionsContext.js';
@@ -50,6 +50,27 @@
50
50
  * designer emits for an unconfigured column list, and supplying the columns is
51
51
  * exactly why a view was named.
52
52
  *
53
+ * The row cap answers "did the component author one?" the same way, and for the
54
+ * same reason (objectui#9899). A cap the contract REFUSES — `pageSize: 0`,
55
+ * a negative, a fraction — is not a cap this relay may treat as the author's
56
+ * intent, so it does not suppress the one the bound view supplies. Deciding
57
+ * that question by PRESENCE is how a view's legitimate cap was dropped in
58
+ * favour of the consuming renderer's own default: measured end to end on
59
+ * `list-view` before the repair, a component carrying `pagination.pageSize: 0`
60
+ * under a view supplying `7` put `$top: 100` on the wire — the view's cap never
61
+ * reached it, so the read was WIDER than the view asked for.
62
+ *
63
+ * The VIEW's own cap can be refused too, and `@object-ui/core` drops it before
64
+ * it reaches this relay (objectui#9928). The drop is right, but it left the
65
+ * consuming renderer nothing to refuse, so it went silent — and this relay
66
+ * reports the view's refusal instead (objectui#10015). See
67
+ * {@link describeRefusedViewRowLimit} for when that message applies.
68
+ *
69
+ * The BINDING's own cap follows the same rule (objectui#10016): a `limit` the
70
+ * contract refuses is not authored, so `@object-ui/core` lets the view's cap
71
+ * through in its place, and that cap is a baseline here like any other
72
+ * view-sourced value. See {@link describeRefusedBindingRowLimit}.
73
+ *
53
74
  * ## What a mapping may NOT do
54
75
  *
55
76
  * {@link ElementDataSourceMapping} names only keys the target block genuinely
@@ -59,6 +80,17 @@
59
80
  * therefore leaves `limit` unmapped rather than parking it somewhere plausible,
60
81
  * and the gap is recorded at the call site instead of being papered over here.
61
82
  *
83
+ * ## A block with no ADAPTER fails loudly too (objectui#5378)
84
+ *
85
+ * The section below is about an unresolvable view NAME. The coarser failure —
86
+ * no runtime adapter reached the block at all — used to render an empty shell
87
+ * in exactly the same silence, and that is what `requiresDataSource` +
88
+ * {@link NoDataSourcePanel} now report. Measured on the published
89
+ * getting-started guide: all five of its blocks resolved no adapter, fetched
90
+ * nothing, and said nothing. The two panels are deliberately the same shape;
91
+ * they answer "which view?" and "which data source?", and an author who sees
92
+ * either one is being told where to look.
93
+ *
62
94
  * ## An unresolvable `view` fails loudly
63
95
  *
64
96
  * When the named view does not exist the gate renders a configuration error
@@ -68,8 +100,8 @@
68
100
  * like it works.
69
101
  */
70
102
  import * as React from 'react';
71
- import { type ElementDataSourceConfig } from '@object-ui/core';
72
- import { type ElementDataSourceStatus } from '../hooks/useElementDataSource';
103
+ import { type ElementDataSourceConfig, type FilterOperatorError } from '@object-ui/core';
104
+ import { type ElementDataSourceStatus } from '../hooks/useElementDataSource.js';
73
105
  /**
74
106
  * Where a block's row cap lives. Three spellings are real in this repo and each
75
107
  * is the ONLY one its block reads: `list-view`/`object-grid` read
@@ -128,6 +160,15 @@ export interface UseElementDataSourceSchemaResult<S> {
128
160
  config?: ElementDataSourceConfig;
129
161
  /** Author-facing explanation, set only for `missing`. */
130
162
  error?: string;
163
+ /**
164
+ * The refused filter that stopped the binding from applying — set only with
165
+ * `missing`, and `error` then carries its message (objectui#10789). Either
166
+ * merge can refuse: the view's filter with the binding's own
167
+ * ({@link useElementDataSource}), or the component's filter with that result
168
+ * (this hook). {@link ElementDataSourceGate} draws the malformed-filter
169
+ * notice from it.
170
+ */
171
+ filterRefusal?: FilterOperatorError;
131
172
  }
132
173
  /**
133
174
  * Resolve a block's `dataSource` binding and apply it to the block's own schema
@@ -169,6 +210,108 @@ export declare function ElementDataSourceErrorPanel({ testId, title, message, }:
169
210
  * would flash a configuration error on every mount.
170
211
  */
171
212
  export declare function ElementDataSourceLoadingPanel({ testId, }: Pick<ElementDataSourceStatusPanelProps, 'testId'>): React.ReactElement;
213
+ /**
214
+ * The adapter this block would use, resolved the one way every block in the
215
+ * family resolves it: an explicit value first, the `SchemaRendererProvider`
216
+ * context second.
217
+ *
218
+ * The `isElementDataSourceConfig` guard is the same one
219
+ * {@link useElementDataSourceSchema} applies — a host handing us the spec
220
+ * BINDING under this name is handing us metadata, not an adapter, and counting
221
+ * it as one is how "no data source" reads as "resolved".
222
+ *
223
+ * `T` is what the CALLER expects to have been injected, and the assertion is
224
+ * the caller's, not this hook's: an adapter arrives here as a `dataSource` prop
225
+ * or a context value that nothing in this package can narrow, so the type has
226
+ * to be declared by whoever knows which interface their block consumes. Leave
227
+ * it off and you get `unknown`, which is the honest default.
228
+ */
229
+ export declare function useResolvedDataSource<T = unknown>(explicit?: unknown): T | undefined;
230
+ /**
231
+ * The author-facing sentence a block with no adapter says out loud.
232
+ *
233
+ * One wording for the whole object-bound family, because the reader's mistake
234
+ * is the same one every time and the fix is the same one every time: the
235
+ * adapter is injected by an ANCESTOR (`SchemaRendererProvider`), so nothing
236
+ * written on the block itself can supply it. Naming the block and the object
237
+ * it was about to read is what turns "nothing happened" into an address.
238
+ */
239
+ export declare function noDataSourceMessage(blockKey: string, objectName?: unknown): string;
240
+ /**
241
+ * The "this block resolved no adapter" panel — objectui#5378 item 2, and the
242
+ * #5349 shape it belongs to.
243
+ *
244
+ * Until this existed, a block that resolved no adapter rendered an EMPTY SHELL:
245
+ * `object-grid` painted a header-only table, `object-form` a field-less card,
246
+ * `detail-view` nothing at all — no error, no warning, no empty state that
247
+ * explained itself. Measured on the published getting-started guide
248
+ * (`content/docs/guide/building-crud-app.md`), every one of its five blocks was
249
+ * in that state and the page reported success while fetching nothing.
250
+ *
251
+ * The empty shell is the defect, not a side effect of it: it is indistinguishable
252
+ * from "the query ran and matched no rows", which is the reading an author (very
253
+ * often an AI author) takes, and it points at the DATA when the fault is in the
254
+ * WIRING one level up. Same posture as {@link ElementDataSourceErrorPanel}: the
255
+ * metadata is answerable, so the answer is rendered where the author can see it.
256
+ */
257
+ export declare function NoDataSourcePanel({ testId, title, message, }: ElementDataSourceStatusPanelProps): React.ReactElement;
258
+ /**
259
+ * The wrapping seam — mark `renderer` as one that wraps {@link
260
+ * ElementDataSourceGate}, so its registrations DECLARE the `dataSource` key this
261
+ * gate READS (objectui#6678).
262
+ *
263
+ * ```tsx
264
+ * export const ObjectMapRenderer = elementDataSourceBlock<React.FC<any>>(({ schema }) => (
265
+ * <ElementDataSourceGate schema={schema} testId="object-map" …>
266
+ * {(bound) => <ObjectMap schema={bound} />}
267
+ * </ElementDataSourceGate>
268
+ * ));
269
+ * ```
270
+ *
271
+ * ## What this closes
272
+ *
273
+ * `PageComponentSchema.dataSource` is the one spelling that resolves a saved
274
+ * view for an object-bound block. It works — and because no registration
275
+ * declared it, `sdui-parser`'s `validateTree` reported it with the SAME
276
+ * `unknown-prop` warning it gives the spellings that do nothing. On the tier
277
+ * built to accept AI-authored pages the diagnostic IS the contract, so the
278
+ * tier's only signal pointed away from the one key that works.
279
+ *
280
+ * ## Why a seam and not nine declarations
281
+ *
282
+ * Maintainer ruling, 2026-08-29: option B **in the injection form**. Nine
283
+ * hand-written copies across nine packages is the shape that ruling refused —
284
+ * they drift, and the tenth block forgets. Passing through here is the only
285
+ * thing a block does; `Registry.register` emits the declaration
286
+ * (`withElementDataSourceInput`), and `ELEMENT_DATA_SOURCE_INPUT` in
287
+ * `@object-ui/core` is its single copy, beside the binding's own semantics.
288
+ *
289
+ * ## And it cannot be forgotten
290
+ *
291
+ * `scripts/check-element-data-source-declaration.mjs` fails any source that
292
+ * renders `ElementDataSourceGate` for a registration without passing that
293
+ * registration's renderer through this function. That is the mechanical half of
294
+ * "a tenth block gets it automatically"; this function is the seam it enforces.
295
+ *
296
+ * Returns the renderer UNCHANGED (the mark is held in a `WeakSet`), so it is safe
297
+ * over a `React.forwardRef` object, over `React.memo`, and over a component that
298
+ * something else already re-exports by reference.
299
+ *
300
+ * ⚠️ Re-exported from `@object-ui/core` — where the marker and the declaration
301
+ * live — rather than wrapped here: ONE function under ONE name. This export
302
+ * exists for DISCOVERABILITY, so the seam is findable beside the gate it is
303
+ * about; **call sites must import it from `@object-ui/core`**, and
304
+ * `check:element-data-source-declaration` enforces that.
305
+ *
306
+ * The rule is measured, not stylistic. A registration runs at MODULE SCOPE, 101
307
+ * suites in this repo partially mock `@object-ui/react` by hand-listing the
308
+ * exports they return, and a module-scope read of a name absent from such a list
309
+ * throws at COLLECTION time — the importing test file dies before running one
310
+ * assertion. Taking the seam from here reddened 17 files across all four CI
311
+ * shards with zero failed assertions among them. Nothing mocks `@object-ui/core`,
312
+ * and every registration module already imports `ComponentRegistry` from it.
313
+ */
314
+ export { elementDataSourceBlock } from '@object-ui/core';
172
315
  export interface ElementDataSourceGateProps<S> {
173
316
  /** The block's schema node. */
174
317
  schema: S;
@@ -180,6 +323,23 @@ export interface ElementDataSourceGateProps<S> {
180
323
  testId: string;
181
324
  /** Heading for the unresolvable-view panel. */
182
325
  errorTitle?: string;
326
+ /**
327
+ * Whether THIS placement cannot do its job without a runtime adapter — when
328
+ * true and none resolves, the gate renders {@link NoDataSourcePanel} instead
329
+ * of the block (objectui#5378 item 2).
330
+ *
331
+ * The gate cannot compute this and does not try. "Needs an adapter" is a
332
+ * statement about the block's own fallbacks, and every block in the family
333
+ * has different ones: an `object-grid` carrying inline `data` rows needs no
334
+ * adapter, an `object-form` with inline `customFields` needs none, a
335
+ * `detail-view` handed a `data` record needs none. A predicate guessed HERE
336
+ * would paint a configuration error over a block that is working, which is a
337
+ * worse failure than the silence this replaces — so the call site, which
338
+ * knows its own fallbacks, states it.
339
+ */
340
+ requiresDataSource?: boolean;
341
+ /** Explanation for the no-adapter panel; see {@link noDataSourceMessage}. */
342
+ noDataSourceMessage?: string;
183
343
  /**
184
344
  * Renders the block with the bound schema. Called during the gate's own
185
345
  * render, so it must RETURN AN ELEMENT and never call hooks itself — the
@@ -196,4 +356,4 @@ export interface ElementDataSourceGateProps<S> {
196
356
  * against a half-resolved query is how a page shows a wider answer than the one
197
357
  * that was authored.
198
358
  */
199
- export declare function ElementDataSourceGate<S>({ schema, mapping, dataSource, testId, errorTitle, children, }: ElementDataSourceGateProps<S>): React.ReactElement | null;
359
+ export declare function ElementDataSourceGate<S>({ schema, mapping, dataSource, testId, errorTitle, requiresDataSource, noDataSourceMessage: noDataSourceText, children, }: ElementDataSourceGateProps<S>): React.ReactElement | null;