diff --git a/.changeset/12364-provider-api-prose.md b/.changeset/12364-provider-api-prose.md new file mode 100644 index 0000000000..f577050eed --- /dev/null +++ b/.changeset/12364-provider-api-prose.md @@ -0,0 +1,20 @@ +--- +'@object-ui/types': patch +'@object-ui/plugin-grid': patch +'@object-ui/plugin-map': patch +'@object-ui/plugin-calendar': patch +'@object-ui/plugin-gantt': patch +'@object-ui/react': patch +'@object-ui/auth': patch +'@object-ui/app-shell': patch +'@object-ui/core': patch +--- + +Published text no longer teaches the retired view-level `provider: 'api'` data source (objectui#12364). A view binds `provider: 'object'` or `provider: 'value'`; a metadata-declared API call is the page element's `dataSource` binding. Text only: no schema accepts or refuses anything new, and no renderer behaves differently. + +- **`@object-ui/types`:** the `object-tree` `data` description drops the `api` provider from the providers it names. +- **`@object-ui/plugin-grid` / `@object-ui/plugin-map`:** the `data` input descriptions drop the `api` provider. +- **`@object-ui/plugin-calendar` / `@object-ui/plugin-gantt` / `@object-ui/plugin-map`:** the error thrown when no `DataSource` is available now reads `DataSource required for the object provider` (was `DataSource required for object/api providers`). It fires in exactly the same conditions as before. +- **`@object-ui/react` / `@object-ui/auth`:** the README and docblocks describe `SchemaRendererProvider`'s `apiFetch` as what it is now: the host's authenticated fetch the renderers use for their own reads of the host's API (chart metadata, dataset dimension labels, record-permission probes). It is not a channel for a view's data source. +- **`@object-ui/app-shell`:** comment wording only in `ConsoleShell`, `useConsoleActionRuntime` and `ObjectView` (these comments ship in the built JS): no behaviour change. +- **`@object-ui/core`:** docblock wording only on `normalizeListViewSchema` and `resolveRecordSourceObjectName` (these ship in `dist/*.d.ts` and the built JS): no behaviour change. diff --git a/apps/console/src/services/approvalRequestsDataSource.test.tsx b/apps/console/src/services/approvalRequestsDataSource.test.tsx index 81388d85b3..61ee81571e 100644 --- a/apps/console/src/services/approvalRequestsDataSource.test.tsx +++ b/apps/console/src/services/approvalRequestsDataSource.test.tsx @@ -2,7 +2,7 @@ * ObjectUI * Copyright (c) 2024-present ObjectStack Inc. * - * objectui#12032 — approval requests through the standard `provider: 'api'` + * objectui#12032 — approval requests through a code-built `ApiDataSource` * door, so `ListView` reads them with `viewer` as a field and the scopes stay * server-side. * diff --git a/content/docs/plugins/plugin-calendar.mdx b/content/docs/plugins/plugin-calendar.mdx index 34ef0fecc8..d2076bf431 100644 --- a/content/docs/plugins/plugin-calendar.mdx +++ b/content/docs/plugins/plugin-calendar.mdx @@ -417,7 +417,7 @@ const valueProviderCalendar: ObjectCalendarSchema = { ⚠️ Since **objectui#8348** a calendar authored with a record-source *config object* under `data` — `{ provider: 'value', items: [...] }`, -`{ provider: 'object', object: … }`, `{ provider: 'api', … }` — is not honoured +`{ provider: 'object', object: … }` — is not honoured here. `object-calendar`'s published `data` row is an **array of pre-fetched records**, so the config object is refused by kind: `os validate` and the save gate reject it, and as of that card the renderer does too. diff --git a/content/docs/plugins/plugin-map.mdx b/content/docs/plugins/plugin-map.mdx index 0de684c13f..3e03e49959 100644 --- a/content/docs/plugins/plugin-map.mdx +++ b/content/docs/plugins/plugin-map.mdx @@ -319,7 +319,8 @@ const staticLocations = { The view-level `data.provider: 'api'` is retired in `@objectstack/spec` (objectstack#23082), and `ObjectMap` has no branch for it: a schema that still carries one plots no markers and requests nothing. Without a `DataSource` it -fails one step earlier, with `DataSource required for object/api providers`. +fails one step earlier, with the error an object-bound map gets: +`DataSource required for the object provider`. A metadata-declared API call is the page element's `dataSource` binding. Read from the database with the **Object Provider** above, or pass records you diff --git a/examples/schema-catalog/test/catalog-gallery-render.test.tsx b/examples/schema-catalog/test/catalog-gallery-render.test.tsx index ac19e7b5b5..5dbbda46d1 100644 --- a/examples/schema-catalog/test/catalog-gallery-render.test.tsx +++ b/examples/schema-catalog/test/catalog-gallery-render.test.tsx @@ -198,7 +198,7 @@ const FAILED_TO_RENDER = 'failed to render'; * draw the primitive, so this string is what would catch them sliding back to * the bare keyword. */ -const DATASOURCE_REQUIRED = 'DataSource required for object/api providers'; +const DATASOURCE_REQUIRED = 'DataSource required for the object provider'; const ALL_DIAGNOSTICS = [UNKNOWN_COMPONENT, FAILED_TO_RENDER, DATASOURCE_REQUIRED]; diff --git a/packages/app-shell/src/console/ConsoleShell.tsx b/packages/app-shell/src/console/ConsoleShell.tsx index 1715d826d5..b2bc6467dd 100644 --- a/packages/app-shell/src/console/ConsoleShell.tsx +++ b/packages/app-shell/src/console/ConsoleShell.tsx @@ -226,11 +226,13 @@ export function ConnectedShell({ children }: { children: ReactNode }) { ); } -// Authenticated fetch for `provider: 'api'` view data sources (#2725): custom -// endpoints get the same Authorization / X-Tenant-ID / Accept-Language headers -// as the native adapter channel, so a cookie whose HMAC signature rotated -// (e.g. dev-server restart) no longer strands them on 401. `sameOriginOnly` -// keeps the bearer token off third-party hosts a view's URL may point at, and +// The host's authenticated fetch, handed to the renderers as `apiFetch` +// (#2725): their own reads of the host's API (chart metadata, dataset +// dimension labels, record-permission probes) get the same Authorization / +// X-Tenant-ID / Accept-Language headers as the native adapter channel, so a +// cookie whose HMAC signature rotated (e.g. dev-server restart) no longer +// strands them on 401. `sameOriginOnly` keeps the bearer token off any +// third-party host a request may name, and // withSettleSignal keeps these requests visible to the ADR-0054 C5 idle probe. const apiProviderFetch = withSettleSignal(createAuthenticatedFetch({ sameOriginOnly: true })); diff --git a/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx b/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx index b8f1c0288f..b7c7d5f63a 100644 --- a/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx +++ b/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx @@ -355,7 +355,7 @@ export function useConsoleActionRuntime(opts: ConsoleActionRuntimeOptions): Cons // ruling): an action's `target` comes from author-supplied metadata and may // name an off-origin host. Cross-origin requests pass through to the bare // global fetch — no Authorization, X-Tenant-ID, or Accept-Language — matching - // the `provider: 'api'` data-source lane (ConsoleShell). Same-origin actions + // the renderers' `apiFetch` lane (ConsoleShell). Same-origin actions // are unaffected. An off-origin integration that legitimately needs the // platform bearer declares itself explicitly or proxies same-origin. const authFetch = useMemo(() => createAuthenticatedFetch({ sameOriginOnly: true }), []); diff --git a/packages/app-shell/src/views/ObjectView.tsx b/packages/app-shell/src/views/ObjectView.tsx index a6c889338b..40bc67fbb7 100644 --- a/packages/app-shell/src/views/ObjectView.tsx +++ b/packages/app-shell/src/views/ObjectView.tsx @@ -3507,7 +3507,7 @@ function ObjectViewInner({ dataSource, objects, onEdit, externalRefreshKey }: Co // The census answers each in `ObjectView.relayRungCensus-7559.test.ts`. // // ViewData source override (spec `data` key): a view authored with - // `data: {provider:'api', read, write}` must survive this explicit + // `data: {provider:'value', items}` must survive this explicit // picklist, or ObjectGantt falls back to provider:'object'. data: (viewDef as any).data ?? (listSchema as any).data, // Propagate new spec properties (P0/P1/P2) diff --git a/packages/auth/README.md b/packages/auth/README.md index f63bbee8b7..8f5b17fab5 100644 --- a/packages/auth/README.md +++ b/packages/auth/README.md @@ -184,10 +184,10 @@ import { createAuthenticatedFetch } from '@object-ui/auth'; const authedFetch = createAuthenticatedFetch(); -// For fetches whose target URL comes from view metadata (`provider: 'api'` -// data sources), restrict credential injection to the current page's origin -// so the platform token never leaks to third-party hosts: -const apiProviderFetch = createAuthenticatedFetch({ sameOriginOnly: true }); +// For fetches whose target URL may come from metadata (an API-call +// action's target), restrict credential injection to the current page's +// origin so the platform token never leaks to third-party hosts: +const sameOriginFetch = createAuthenticatedFetch({ sameOriginOnly: true }); ``` ### getSessionOwnerChangeCount / subscribeSessionOwnerChange diff --git a/packages/auth/src/__tests__/createAuthenticatedFetch.test.tsx b/packages/auth/src/__tests__/createAuthenticatedFetch.test.tsx index 1adc9b4802..eb6c445f87 100644 --- a/packages/auth/src/__tests__/createAuthenticatedFetch.test.tsx +++ b/packages/auth/src/__tests__/createAuthenticatedFetch.test.tsx @@ -84,8 +84,8 @@ describe('createAuthenticatedFetch', () => { expect(calls[0].headers.get('Accept-Language')).toBe('ja'); }); - // ── sameOriginOnly (#2725) — provider:'api' fetches must not leak the - // bearer token to third-party hosts a view's metadata URL may name ── + // ── sameOriginOnly (#2725) — fetches must not leak the bearer token to + // third-party hosts a metadata URL (an action's target) may name ── it('sameOriginOnly: still injects the token on same-origin API calls', async () => { vi.spyOn(TokenStorage, 'get').mockReturnValue('tok123'); diff --git a/packages/auth/src/__tests__/impersonation-lane-4467.test.tsx b/packages/auth/src/__tests__/impersonation-lane-4467.test.tsx index 6276876330..5d064df33c 100644 --- a/packages/auth/src/__tests__/impersonation-lane-4467.test.tsx +++ b/packages/auth/src/__tests__/impersonation-lane-4467.test.tsx @@ -8,7 +8,7 @@ * auth lane — `createBearerFetch` inside `createAuthClient`, used by * sign-in / get-session / the auth endpoints; * data lane — `createAuthenticatedFetch`, used by the adapter, by - * `provider: 'api'` view data sources, and by EVERY metadata + * the renderers' `apiFetch`, and by EVERY metadata * `type: 'api'` action (`useConsoleActionRuntime`'s `apiHandler`). * * Only the auth lane read the `set-auth-token` response header better-auth's diff --git a/packages/auth/src/createAuthenticatedFetch.ts b/packages/auth/src/createAuthenticatedFetch.ts index bf273649b8..97f3515ccd 100644 --- a/packages/auth/src/createAuthenticatedFetch.ts +++ b/packages/auth/src/createAuthenticatedFetch.ts @@ -41,9 +41,10 @@ export interface CreateAuthenticatedFetchOptions { * current page are passed through to the bare global fetch — no * Authorization, X-Tenant-ID, or Accept-Language headers are attached. * - * Use this for fetches whose target URL comes from view metadata - * (`provider: 'api'` data sources): those may point at third-party hosts - * that must never see the platform bearer token. + * Use this for fetches whose target URL may come from metadata (a + * `type: 'api'` action's target) or that a host hands to the renderers as + * `apiFetch`: those may point at third-party hosts that must never see the + * platform bearer token. */ sameOriginOnly?: boolean; } @@ -155,7 +156,7 @@ export function createAuthenticatedFetch( // // The console injects the same localStorage bearer from two places: the // AUTH lane (sign-in / get-session / the auth endpoints) and THIS one — the - // adapter, `provider: 'api'` data sources, and every metadata `type: 'api'` + // adapter, the renderers' `apiFetch`, and every metadata `type: 'api'` // action (`useConsoleActionRuntime`'s `apiHandler`). better-auth's // server-side bearer plugin hands a rotated session token back in // `set-auth-token` on whichever lane the call arrived over. Only the auth diff --git a/packages/core/src/utils/__tests__/normalize-list-view.test.ts b/packages/core/src/utils/__tests__/normalize-list-view.test.ts index ac8da7e864..49d59c9982 100644 --- a/packages/core/src/utils/__tests__/normalize-list-view.test.ts +++ b/packages/core/src/utils/__tests__/normalize-list-view.test.ts @@ -398,8 +398,8 @@ describe('normalizeListViewSchema (#2890)', () => { }); it('KEEPS `data` — unlike every other fold here, and deliberately', () => { - // `data` is not a legacy alias with one meaning: it has four providers, - // `api`/`value` are read live in ListView, and the whole block is + // `data` is not a legacy alias with one meaning: it has more providers + // than `object`, `value` is read live in ListView, and the whole block is // forwarded to child views whose own `getDataConfig` reads it BEFORE // `objectName`. Deleting it for one provider would rewrite what a child // resolves. See the note on `normalizeListViewSchema`. diff --git a/packages/core/src/utils/normalize-list-view.ts b/packages/core/src/utils/normalize-list-view.ts index dda379f7d2..2f29bf8989 100644 --- a/packages/core/src/utils/normalize-list-view.ts +++ b/packages/core/src/utils/normalize-list-view.ts @@ -427,8 +427,8 @@ function readListViewKind(value: unknown): string | undefined { * · an already-present `objectName` WINS — this fold only fills a gap, it * never re-points a binding that already resolves; * · `data` is NOT deleted. Every other fold deletes because the legacy key - * has one meaning and one home; `data` has four providers, `api`/`value` - * are read live in `ListView`, and the whole block is FORWARDED to child + * has one meaning and one home; `data` has more providers than + * `object`, `value` is read live in `ListView`, and the whole block is FORWARDED to child * views (the gantt branch), whose own `getDataConfig` reads `data` before * `objectName`. Deleting it for one provider would rewrite what a child * resolves. Nothing in `ListView` reads `data.provider === 'object'`, so diff --git a/packages/core/src/utils/record-source.ts b/packages/core/src/utils/record-source.ts index b9e3dfd7d8..b28e041dda 100644 --- a/packages/core/src/utils/record-source.ts +++ b/packages/core/src/utils/record-source.ts @@ -47,7 +47,7 @@ import type { ViewData } from '@object-ui/types'; * The ladder's second rung wraps inline rows as `{ provider: 'value', items }`, * which names no object at all. So for the object-NAME question the three-rung * ladder reduces to two rungs — the resolved config's object when it names one, - * else the schema's own `objectName`, which is what a `value`/`api`-backed block + * else the schema's own `objectName`, which is what a `value`-backed block * still needs for metadata reads, i18n field labels and permission verdicts. * Callers pass the ALREADY-RESOLVED config (their * {@link resolveRecordSourceConfig} output, direct or through a local wrapper), diff --git a/packages/plugin-calendar/src/ObjectCalendar.tsx b/packages/plugin-calendar/src/ObjectCalendar.tsx index 26ee66489b..79d48adcc9 100644 --- a/packages/plugin-calendar/src/ObjectCalendar.tsx +++ b/packages/plugin-calendar/src/ObjectCalendar.tsx @@ -849,7 +849,7 @@ export const ObjectCalendar: React.FC = ({ // inline set has no metadata to build and behind the // `objectSchemaReady` gate deliberately scoped to that same arm. // Falling through here would therefore throw - // `DataSource required for object/api providers` on a calendar that + // `DataSource required for the object provider` on a calendar that // needs no DataSource at all. So the adapter is resolved for the // inline provider ONLY, and the same three keys are lowered onto the same query shape. // @@ -892,7 +892,7 @@ export const ObjectCalendar: React.FC = ({ } if (!dataSource || typeof dataSource.find !== 'function') { - throw new Error('DataSource required for object/api providers'); + throw new Error('DataSource required for the object provider'); } if (dataProvider === 'object') { diff --git a/packages/plugin-charts/src/ObjectChart.tsx b/packages/plugin-charts/src/ObjectChart.tsx index 9bdf6c1ff5..b947750598 100644 --- a/packages/plugin-charts/src/ObjectChart.tsx +++ b/packages/plugin-charts/src/ObjectChart.tsx @@ -608,8 +608,8 @@ export const ObjectChart = (props: ObjectChartProps) => { // Resolve the category dimension's option colors (P3). Best-effort: any // failure leaves categoryColors null and the chart keeps the theme palette. // - // Both metadata reads ride the host's AUTHENTICATED fetch (#4114) — the same - // channel `provider: 'api'` view sources use — falling back to the global one + // Both metadata reads ride the host's AUTHENTICATED fetch (#4114) — the + // `SchemaRendererProvider`'s `apiFetch` — falling back to the global one // only when no host supplies it. A bearer-token session carries its // credential in the `Authorization` header, not a cookie, so // `credentials: 'include'` alone left these two reads unauthenticated in a diff --git a/packages/plugin-detail/src/useRecordEditable.ts b/packages/plugin-detail/src/useRecordEditable.ts index ee79371866..b723fdf44a 100644 --- a/packages/plugin-detail/src/useRecordEditable.ts +++ b/packages/plugin-detail/src/useRecordEditable.ts @@ -30,7 +30,7 @@ * user cannot act. * * The probe rides the host's AUTHENTICATED fetch (`SchemaRendererProvider`'s - * `apiFetch`, the same channel `provider: 'api'` view sources use), not the bare + * `apiFetch`), not the bare * global one. A bearer-token session carries its credential in the * `Authorization` header, not a cookie, so `credentials: 'include'` alone made * every probe 401 on a perfectly valid admin session — the verdict then always diff --git a/packages/plugin-gantt/src/ObjectGantt.test.tsx b/packages/plugin-gantt/src/ObjectGantt.test.tsx index 6ff0709e69..454356602d 100644 --- a/packages/plugin-gantt/src/ObjectGantt.test.tsx +++ b/packages/plugin-gantt/src/ObjectGantt.test.tsx @@ -186,8 +186,8 @@ describe('ObjectGantt', () => { expect(screen.getByTestId('gv-field-1-1').textContent).toBe('Tags=a, b'); }); - it('falls back to quickFilters labels + ISO date sniffing when there is no object schema (api provider)', async () => { - // The api provider returns records but no object schema, so tooltip fields + it('falls back to quickFilters labels + ISO date sniffing when there is no object schema (inline value rows)', async () => { + // Inline `value` rows come with no object schema, so tooltip fields // have no defs: machine values and raw ISO strings used to leak through. const schema: any = { type: 'gantt', diff --git a/packages/plugin-gantt/src/ObjectGantt.tsx b/packages/plugin-gantt/src/ObjectGantt.tsx index ffa6f0b37e..51ab2a7840 100644 --- a/packages/plugin-gantt/src/ObjectGantt.tsx +++ b/packages/plugin-gantt/src/ObjectGantt.tsx @@ -1022,7 +1022,7 @@ export const ObjectGantt: React.FC = ({ // resolved; what it is not is a reason to rebuild this callback. const adapter = effectiveDataSourceRef.current; if (!adapter || typeof adapter.find !== 'function') { - throw new Error('DataSource required for object/api providers'); + throw new Error('DataSource required for the object provider'); } // 'object' → the context adapter (resolved above). diff --git a/packages/plugin-gantt/src/__tests__/ObjectGantt.noSchemaZone-10866.test.tsx b/packages/plugin-gantt/src/__tests__/ObjectGantt.noSchemaZone-10866.test.tsx index 601460eeb7..85112d3c3e 100644 --- a/packages/plugin-gantt/src/__tests__/ObjectGantt.noSchemaZone-10866.test.tsx +++ b/packages/plugin-gantt/src/__tests__/ObjectGantt.noSchemaZone-10866.test.tsx @@ -20,7 +20,7 @@ * and a bar handed back ending on a day's midnight is written as the day * before it, the day it runs through. Two * adapters have no types to give: one with no `getObjectSchema`, and one whose - * schema carries no fields (the `api` provider's adapter answers + * schema carries no fields (a code-built `ApiDataSource` answers * `{ fields: {} }`). A chart with a business `timeZone` takes the same split * through the shim, as the schema-declared pin beside this one measures. * diff --git a/packages/plugin-grid/src/index.tsx b/packages/plugin-grid/src/index.tsx index ff9102477b..cf87047c48 100644 --- a/packages/plugin-grid/src/index.tsx +++ b/packages/plugin-grid/src/index.tsx @@ -299,7 +299,7 @@ const GRID_QUERY_INPUTS: ComponentInput[] = [ { name: 'sort', type: 'array', description: 'Initial sort order, `[{ field, order }]`. The only sort spelling this block reads — the retired single-sort `defaultSort` is refused by the protocol and ignored by the renderer.' }, { name: 'pagination', type: 'object', description: 'Pagination config, `{ pageSize, pageSizeOptions, … }`. Presence enables paging with the object\'s settings, and an explicit off wins — the deprecated flat `showPagination: false` turns paging off even beside this object, because this object declares no off switch of its own. Prefer it over the deprecated flat `pageSize` / `showPagination` pair.' }, { name: 'searchableFields', type: 'array', of: 'string', description: 'Fields the toolbar search box queries. A non-empty list is what enables search — prefer it over the deprecated boolean `showSearch`, which cannot say WHICH fields to search.' }, - { name: 'data', type: 'object', description: 'Data source configuration — a `ViewData` object discriminated by `provider`: `{ provider: "object", object }` (what an omitted `data` falls back to, using `objectName`), `{ provider: "api", read, write }`, `{ provider: "value", items: [...] }` for inline rows that bypass the object query, or `{ provider: "schema", schemaId }`. The canonical spelling — the deprecated `staticData` is the array-only shortcut for the `value` provider, so inline rows go under `items` here rather than in a bare array.' }, + { name: 'data', type: 'object', description: 'Data source configuration — a `ViewData` object discriminated by `provider`: `{ provider: "object", object }` (what an omitted `data` falls back to, using `objectName`), `{ provider: "value", items: [...] }` for inline rows that bypass the object query, or `{ provider: "schema", schemaId }`. The canonical spelling — the deprecated `staticData` is the array-only shortcut for the `value` provider, so inline rows go under `items` here rather than in a bare array.' }, // ── presentation ────────────────────────────────────────────────────────── { name: 'rowHeight', type: 'enum', enum: ['compact', 'short', 'medium', 'tall', 'extra_tall'], description: 'Row density. An unrecognised value falls back to `compact` rather than erroring.' }, { name: 'frozenColumns', type: 'number', description: 'How many leading columns stay pinned while the grid scrolls horizontally.' }, diff --git a/packages/plugin-list/src/ListViewBlock.tsx b/packages/plugin-list/src/ListViewBlock.tsx index 7e7f906dda..0e10e61c47 100644 --- a/packages/plugin-list/src/ListViewBlock.tsx +++ b/packages/plugin-list/src/ListViewBlock.tsx @@ -122,7 +122,7 @@ const ListViewBlock = elementDataSourceBlock(React.forwardRef diff --git a/packages/plugin-map/src/ObjectMap.tsx b/packages/plugin-map/src/ObjectMap.tsx index 1947b13116..a8ddf00a6a 100644 --- a/packages/plugin-map/src/ObjectMap.tsx +++ b/packages/plugin-map/src/ObjectMap.tsx @@ -977,7 +977,7 @@ export const ObjectMap: React.FC = ({ // shared query. This effect's `find` sits INSIDE the // `dataProvider === 'object'` arm, behind an `$expand` projection an // inline set has no metadata to build. Falling through here would - // therefore throw `DataSource required for object/api providers` on a + // therefore throw `DataSource required for the object provider` on a // map that needs no DataSource at all. So the adapter is resolved for // the inline provider ONLY, and the same three keys are lowered onto the same query shape. // @@ -1014,7 +1014,7 @@ export const ObjectMap: React.FC = ({ } if (!dataSource || typeof dataSource.find !== 'function') { - throw new Error('DataSource required for object/api providers'); + throw new Error('DataSource required for the object provider'); } if (dataProvider === 'object') { diff --git a/packages/plugin-map/src/index.recordSourceInputs-10394.test.tsx b/packages/plugin-map/src/index.recordSourceInputs-10394.test.tsx index 5baba85bfc..750d6c9d26 100644 --- a/packages/plugin-map/src/index.recordSourceInputs-10394.test.tsx +++ b/packages/plugin-map/src/index.recordSourceInputs-10394.test.tsx @@ -35,10 +35,10 @@ * `objectName`; neither inline source queries the object. * 7. A bare array under `data` is not a record source: `staticData` beside it * is what is plotted. - * 8. The `api` provider plots no markers here, and still shadows `staticData`. - * This row pins a GAP, on purpose: the `data` description says so, and the - * day the provider is implemented this row goes red and the description - * has to change with it. + * 8. A block that still says the retired view-level `api` provider + * (objectstack#23082, objectui#12325) plots no markers here, queries + * nothing, and still shadows `staticData`. The `data` description does not + * name it: a view binds `object` or `value` (objectui#12364). * * `filter` / `sort` on the inline rungs are pinned by * `ObjectMap.inlineQueryKeys-9061.test.tsx` (its `staticDataSpelling` row), @@ -109,7 +109,6 @@ const POSITION_PHRASES: Record = { '`{ provider, … }` data-source configuration', 'read FIRST', 'never reaches `staticData`', - 'The `api` provider is not implemented on the map', 'A bare array is not this key’s shape', ], staticData: ['read SECOND', 'a `data` configuration wins', '`objectName` is read AFTER it'], diff --git a/packages/plugin-map/src/index.tsx b/packages/plugin-map/src/index.tsx index 50ac8ca93d..9f823ca49b 100644 --- a/packages/plugin-map/src/index.tsx +++ b/packages/plugin-map/src/index.tsx @@ -114,7 +114,7 @@ ComponentRegistry.register('object-map', ObjectMapRenderer, { inputs: [ { name: 'objectName', type: 'string', description: 'ObjectQL object name. The record source is one of `data`, `staticData` and `objectName`, or the node’s `dataSource` binding; the `object-map` schema refuses a block that declares none of them.' }, { name: 'map', type: 'object', description: 'The field configuration: `latitudeField` / `longitudeField` (or one `locationField` holding a `"lat,lng"` string, a `{ lat, lng }` object or a `[lat, lng]` pair) place each record’s marker, and a record with no readable coordinates is not plotted; `titleField` and `descriptionField` name what the marker popup shows; `zoom` and `center` (`[latitude, longitude]`) set the initial camera, which otherwise fits the markers; `style` is a MapLibre style URL, read after the node’s `mapStyle`.' }, - { name: 'data', type: 'object', description: 'A `{ provider, … }` data-source configuration, read FIRST on the record-source ladder: a map carrying one never reaches `staticData` and never queries `objectName`. `{ provider: \'value\', items }` plots those rows and `{ provider: \'object\', object }` queries that object, both narrowed by `filter` and ordered by `sort`. The `api` provider is not implemented on the map and plots no markers. A bare array is not this key’s shape and is not a record source: the map falls through to `staticData`, then `objectName`, so inline rows belong under `staticData`.' }, + { name: 'data', type: 'object', description: 'A `{ provider, … }` data-source configuration, read FIRST on the record-source ladder: a map carrying one never reaches `staticData` and never queries `objectName`. `{ provider: \'value\', items }` plots those rows and `{ provider: \'object\', object }` queries that object, both narrowed by `filter` and ordered by `sort`. A bare array is not this key’s shape and is not a record source: the map falls through to `staticData`, then `objectName`, so inline rows belong under `staticData`.' }, { name: 'staticData', type: 'array', description: 'Inline records, read SECOND on the record-source ladder: a `data` configuration wins and this key is then never reached, while `objectName` is read AFTER it, so a map carrying both plots these rows and never queries that object. `filter` and `sort` narrow and order these rows exactly as they do fetched ones.' }, { name: 'filter', type: 'array', description: 'Base query filter in the rule-array form `[{ field, operator, value }, ...]`, narrowing the markers the map plots — fetched rows and inline (`staticData`, `{ provider: \'value\' }`) rows alike. Context tokens such as `{current_user_id}` are resolved first, then the filter is lowered to `$filter` on the query. The MongoDB-style record form is not this key’s shape.' }, { name: 'sort', type: 'array', description: 'Marker order in `[{ field, order }]` form, ordering the rows the map plots. Lowered to `$orderby` on the same query.' }, diff --git a/packages/plugin-tree/src/ObjectTree.tsx b/packages/plugin-tree/src/ObjectTree.tsx index 9a76f60b49..56774d870a 100644 --- a/packages/plugin-tree/src/ObjectTree.tsx +++ b/packages/plugin-tree/src/ObjectTree.tsx @@ -793,7 +793,7 @@ export const ObjectTree: React.FC = ({ const dataProvider = dataConfig?.provider; // NOT a delegation site for `resolveRecordSourceObjectName` (`b041b9c0c`): // this is the data config's OWN object, deliberately `undefined` for every - // other provider so an `api`/`value` tree's `objectName` changing cannot move + // other provider so a `value` tree's `objectName` changing cannot move // this dependency. The shared reader's second rung would put `objectName` // here and re-run the record fetch on a value it does not read. const dataObjectName = dataConfig?.provider === 'object' ? dataConfig.object : undefined; diff --git a/packages/plugin-view/src/ObjectView.tsx b/packages/plugin-view/src/ObjectView.tsx index 93d6708714..f96f6f982c 100644 --- a/packages/plugin-view/src/ObjectView.tsx +++ b/packages/plugin-view/src/ObjectView.tsx @@ -3108,8 +3108,8 @@ export const ObjectView: React.FC = ({ fieldTextColor: (schema as any).fieldTextColor, prefixField: (schema as any).prefixField, showDescription: activeView?.showDescription ?? (schema as any).showDescription, - // ViewData source override (spec `data` key) — e.g. gantt views fed - // by a composite api endpoint; without this pick the api provider + // ViewData source override (spec `data` key) — e.g. a gantt view on + // inline `provider: 'value'` rows; without this pick the view's `data` // never reaches the renderer. // objectui#8980 / objectui#7928's open half: the cast on the // named-view config is GONE — `data` is a declared `NamedListView` diff --git a/packages/react/README.md b/packages/react/README.md index c42f55511b..7ed20c00d4 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -131,10 +131,11 @@ declare const schema: DeclaredNode diff --git a/packages/react/src/context/SchemaRendererContext.tsx b/packages/react/src/context/SchemaRendererContext.tsx index 06985ddd51..1ea323da4a 100644 --- a/packages/react/src/context/SchemaRendererContext.tsx +++ b/packages/react/src/context/SchemaRendererContext.tsx @@ -4,10 +4,13 @@ import type { DataSource } from '@object-ui/types'; import { usePredicateScope } from '../hooks/useExpression.js'; /** - * Host-provided fetch used for `provider: 'api'` view data sources so custom - * endpoints carry the same credentials (Authorization, tenant, locale headers) - * as the native data channel. Optional — when absent, ApiDataSource falls back - * to the bare global fetch (cookies only). + * Host-provided fetch the renderers use for their own reads of the host's API + * (a chart's metadata reads, dataset dimension labels, record-permission + * probes), so those reads carry the same credentials (Authorization, tenant, + * locale headers) as the native data channel. Optional — when absent, those + * reads fall back to the bare global fetch (cookies only). A view does not + * reach it: a view binds `provider: 'object'` or `provider: 'value'`, and a + * code-built `ApiDataSource` takes its own `fetch` in its config. */ export type ApiFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise; @@ -60,7 +63,7 @@ export const SchemaRendererProvider = ({ }) => { // Nested providers (react-page, studio preview surfaces) re-wrap with their // own dataSource but rarely know about the host's authenticated fetch — - // inherit it from the parent context so provider:'api' auth survives nesting. + // inherit it from the parent context so the host's auth survives nesting. const parent = useContext(SchemaRendererContext); const effectiveApiFetch = apiFetch ?? parent?.apiFetch; const value = useMemo( diff --git a/packages/types/src/objectql.ts b/packages/types/src/objectql.ts index e5e4badf05..2527ea2700 100644 --- a/packages/types/src/objectql.ts +++ b/packages/types/src/objectql.ts @@ -336,7 +336,7 @@ export type GanttConfig = SpecGanttConfig & { lockField?: string; /** * Record field carrying the row's OBJECT API NAME. Mixed-object - * trees (an `api` provider composing parent-object rows with child-object rows) + * trees (one source serving parent-object rows beside child-object rows) * need the detail drawer and its full-page link to follow each row's REAL * object — otherwise a child row's `→` link builds a URL under the view's bound * object and 404s. Empty/missing value → falls back to the bound object. diff --git a/packages/types/src/zod/objectql.zod.ts b/packages/types/src/zod/objectql.zod.ts index 76f108b5df..a7bc433ff9 100644 --- a/packages/types/src/zod/objectql.zod.ts +++ b/packages/types/src/zod/objectql.zod.ts @@ -2792,7 +2792,7 @@ const OBJECT_TREE_NEITHER_CHANNEL = neitherContentChannelGuidance( export const ObjectTreeSchema = BaseSchema.extend({ type: z.literal('object-tree'), objectName: z.string().optional().describe('ObjectQL object name — the THIRD record source resolveRecordSourceConfig resolves, after data and staticData; one of the three must be present'), - data: ViewDataSchema.optional().describe('Data source configuration — read FIRST by resolveRecordSourceConfig; the value and object providers draw rows, the api and schema providers draw none on the tree'), + data: ViewDataSchema.optional().describe('Data source configuration — read FIRST by resolveRecordSourceConfig; the value and object providers draw rows, the schema provider draws none on the tree'), staticData: z.array(z.any()).optional().describe('Inline records — read SECOND by resolveRecordSourceConfig, wrapped into a { provider: value } config'), tree: stripImportedDefaults(SpecTreeConfigSchema).optional().describe('Tree field configuration (parentField, labelField, fields, defaultExpandedDepth) — the spec TreeConfig, by reference'), navigation: stripImportedDefaults(SpecNavigationConfigSchema).optional().describe('Row-click navigation — the spec NavigationConfig, by reference; an absent key opens nothing on a standalone tree'),