Skip to content

Commit c6d3f38

Browse files
committed
feat(plugin-map)!: retire the bare map node type key; object-map is the one spelling
Unregister `ComponentRegistry.register('map', ...)` (and with it `view:map`) from @object-ui/plugin-map, following the objectui#8008 family ruling that retired the bare `gantt` / `kanban` keys. No schema face declares `map` as a component node type, so unregistering is the whole retirement; the stored `NamedListView.type` value `map` is untouched (ObjectView / ListView already emit `object-map` for it). - console: drop the lazy `map` stub. - core: drop the `map` / `view:map` rows from RECORD_SOURCE_DATA_ARM_BY_TYPE. - cli: regenerate known-schema-types (bare `map` leaves the list). - prompts: teach `object-map` instead of `view:map` on the list-view Keys line. - README: one registered type; a bare array under `data` is not a record source. - tests: drop the retired rows from the registration / arm pins; add the retirement pin with a firing control on `object-map`. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
1 parent 09ab32b commit c6d3f38

15 files changed

Lines changed: 284 additions & 77 deletions
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
'@object-ui/plugin-map': minor
3+
'@object-ui/core': patch
4+
'@object-ui/cli': patch
5+
'@object-ui/console': patch
6+
---
7+
8+
**BREAKING (node type key):** `@object-ui/plugin-map` no longer registers the bare
9+
`map` node type key, and its namespaced twin `view:map` goes with it. A node
10+
authored `"type": "map"` no longer resolves a renderer. Write
11+
`"type": "object-map"` — the one spelling the plugin serves (objectui#10393,
12+
executing the objectui#8008 family ruling of 2026-09-09 that retired the bare
13+
`gantt` and `kanban` keys).
14+
15+
**Why.** The registry mounted a `map` node while the published declaration refused
16+
it: `ObjectMapSchema.type` is the literal `'object-map'` and `AnyComponentSchema`
17+
has no `map` arm, so `safeValidateSchema` answered a `map` node with
18+
`invalid_union` while the html tier accepted it. Two faces, opposite verdicts.
19+
No schema face ever declared `map` as a component node type, so there is no arm
20+
to turn into a named refusal: unregistering is the whole retirement, exactly as
21+
it was for `gantt`. After it, the html tier (`validateTree` over the live
22+
registry) reports a `map` node as `unknown-component`, matching the Zod face.
23+
24+
**⛔ No stored document moves.** The string `map` names two different things at
25+
two different layers, and only one of them is retiring:
26+
27+
| layer | value | who writes it | retired? |
28+
| --- | --- | --- | --- |
29+
| stored `NamedListView.type` / `defaultViewType` | `"map"` | `CreateViewDialog`, persisted per tenant | **no — untouched** |
30+
| node type key | `map`, `view:map` | hand-authored JSON | **yes** |
31+
32+
`ObjectView` and `ListView` map a stored `map` view onto the node type they
33+
render, and both already emit `object-map`, so every map view created through
34+
the console keeps rendering. Nothing in a tenant database changes, and ⛔ nothing
35+
should be migrated there.
36+
37+
**What moved with it.**
38+
39+
- `@object-ui/console` drops its lazy `map` / `view:map` registration stub; the
40+
lazy `object-map` stub is unchanged.
41+
- `@object-ui/core`: `recordSourceDataArmForType` no longer lists the `map` and
42+
`view:map` rows, since no block is registered under either key.
43+
- `@object-ui/cli`: the generated known-type list that `objectui check` reads no
44+
longer contains bare `map`, so the check now flags it. ⚠️ `view:map` stays on
45+
that list, because the opt-in protocol placeholder (`registerPlaceholders()`
46+
in `@object-ui/components`) registers it — in the console a `view:map` node
47+
renders that placeholder panel, not a map, and `objectui check` does not flag
48+
it. Search documents for `view:map` directly.
49+
50+
The `@object-ui/plugin-map` README now describes one registered type, and its
51+
sentence claiming a bare array under `data` reaches the in-memory adapter is
52+
corrected: a bare array under `data` is not a record source on the map
53+
(objectui#8348), so inline rows belong under `staticData`.

‎.github/prompts/component.prompt.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,14 @@ Responsible for rendering records. The specific `type` determines the Props cont
8787
> ⚠️ **A `Keys` entry is a REGISTRY key; a `Required Types` entry is a spec `type` value. They are
8888
> not the same vocabulary and they have diverged.** `{ "type": "kanban" }` inside a `ListView`
8989
> config is spec-valid, but the board component is registered as `object-kanban` — the namespaced
90-
> `view:kanban` and `view:gantt` spellings retired with the bare `kanban` / `gantt` registrations and
91-
> now answer only the opt-in protocol PLACEHOLDER panel. A document naming one passes
90+
> `view:kanban`, `view:gantt` and `view:map` spellings retired with the bare `kanban` / `gantt` / `map`
91+
> registrations and now answer only the opt-in protocol PLACEHOLDER panel. A document naming one passes
9292
> `objectui check` and then draws nothing. Write the key from the `Keys` bullet, and where a
9393
> presentation is a config value rather than a component, write it as a prop. Enforced by
9494
> `pnpm check:prompt-keys`.
9595
9696
#### 1. List Views (Collection)
97-
* **Keys:** `view:grid`, `object-kanban`, `view:map`, `view:calendar`, `object-gantt`, etc.
97+
* **Keys:** `view:grid`, `object-kanban`, `object-map`, `view:calendar`, `object-gantt`, etc.
9898
* **Contract:** Must implement `ListViewComponentProps`.
9999
```typescript
100100
type ListViewComponentProps = {

‎apps/console/src/register-plugins.ts‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ ComponentRegistry.registerLazy('object-map', () => import('@object-ui/plugin-map
4444
namespace: 'plugin-map',
4545
category: 'view',
4646
});
47-
ComponentRegistry.registerLazy('map', () => import('@object-ui/plugin-map'), {
48-
namespace: 'view',
49-
category: 'view',
50-
});
47+
// ⛔ The bare `map` node type key is RETIRED (objectui#10393, executing the
48+
// objectui#8008 family ruling of 2026-09-09) — `object-map` above is the
49+
// surviving spelling. The STORED `NamedListView.type` value `map` is a
50+
// different layer and is untouched: `ObjectView`'s `switch (viewType)` already
51+
// emits `object-map` for it.
5152

5253
ComponentRegistry.registerLazy('object-tree', () => import('@object-ui/plugin-tree'), {
5354
namespace: 'plugin-tree',

‎packages/cli/src/utils/known-schema-types.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,6 @@ export const KNOWN_SCHEMA_TYPES: readonly string[] = [
231231
'location',
232232
'lookup',
233233
'main',
234-
'map',
235234
'mark',
236235
'markdown',
237236
'marketplace:installed-list',

‎packages/core/src/utils/record-source.ts‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,9 @@ export function resolveRecordSourceObjectName(
100100
* a block the ruling does not decide, and it is reported rather than guessed.
101101
*
102102
* The arm is passed BY THE CALL SITE rather than looked up from `schema.type`
103-
* on purpose. Every one of these renderers is registered twice — `object-grid`
103+
* on purpose. Most of these renderers are registered twice — `object-grid`
104104
* and the `view:grid` alias `grid`, `object-calendar` and `calendar`, and so on
105+
* (the bare `gantt` and `map` keys are retired, objectui#8008 / objectui#10393)
105106
* — so a node reaches the same component under either spelling, and a table
106107
* keyed by `type` would answer for one tag and silently miss the other. A
107108
* REQUIRED parameter makes the arm a compile-time obligation at each of the
@@ -317,8 +318,8 @@ export function resolveRecordSourceConfig<Arm extends RecordSourceDataArm>(
317318
* ## Why a type-keyed table exists beside the REQUIRED parameter
318319
*
319320
* {@link RecordSourceDataArm}'s docblock states, correctly, why
320-
* {@link resolveRecordSourceConfig} takes the arm as a required PARAMETER: each
321-
* of these renderers is registered under two spellings, a node reaches the same
321+
* {@link resolveRecordSourceConfig} takes the arm as a required PARAMETER: most
322+
* of these renderers are registered under two spellings, a node reaches the same
322323
* component under either, and a table keyed by `type` would answer for one tag
323324
* and silently miss the other. That argument is about a call site that already
324325
* has one block in front of it — there a parameter is strictly better, and it
@@ -345,7 +346,7 @@ export function resolveRecordSourceConfig<Arm extends RecordSourceDataArm>(
345346
* ⚠️ ONE `register()` CALL PRODUCES SEVERAL KEYS, and a row must be written for
346347
* each of them — `SchemaRenderer` looks this table up with the raw
347348
* `schema.type`, which is whatever spelling the author wrote. A registration
348-
* with `namespace: 'view'` is reachable as `view:map` AND as bare `map`; one
349+
* with `namespace: 'view'` is reachable as `view:calendar` AND as bare `calendar`; one
349350
* with `skipFallback` is reachable ONLY under its namespaced key. MEASURED via
350351
* `ComponentRegistry.getAllTypes()` with the five plugins loaded, grouped by
351352
* the renderer each key resolves to.
@@ -366,7 +367,9 @@ export function resolveRecordSourceConfig<Arm extends RecordSourceDataArm>(
366367
* it does not compute one.
367368
*
368369
* - `ObjectGrid.tsx`'s `getDataConfig` — `'view-data'`.
369-
* - `ObjectMap.tsx`'s `getDataConfig` — `'view-data'`.
370+
* - `ObjectMap.tsx`'s `getDataConfig` — `'view-data'`. One block spelling
371+
* only: the bare `map` key (and with it `view:map`) is retired
372+
* (objectui#10393).
370373
* - `ObjectGantt.tsx`'s `rawDataConfig` — `'view-data'`. One block spelling
371374
* only: the bare `gantt` key is retired (objectui#8008).
372375
* - `ObjectCalendar.tsx`'s `dataConfig` — `'array'`.
@@ -383,8 +386,6 @@ const RECORD_SOURCE_DATA_ARM_BY_TYPE: Readonly<Record<string, RecordSourceDataAr
383386
'view:grid': 'view-data',
384387
'object-map': 'view-data',
385388
'plugin-map:object-map': 'view-data',
386-
'view:map': 'view-data',
387-
map: 'view-data',
388389
'object-gantt': 'view-data',
389390
'plugin-gantt:object-gantt': 'view-data',
390391
// — array arm: `z.array(...)`, pre-fetched records —

‎packages/plugin-map/README.md‎

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,22 @@ marker comes from a record's own coordinate fields, and the first paint frames t
77
records that were fetched. It is a *view over data* — there is no authored marker
88
list, and no pin you place by hand.
99

10-
Importing the package registers two component types on the `ComponentRegistry`,
11-
both resolving to the same renderer:
10+
Importing the package registers one component type on the `ComponentRegistry`:
11+
`object-map`, the object-bound renderer. Inside an `ObjectView`, a stored `map`
12+
view is compiled to an `object-map` node, so a saved map view ends at the same
13+
component.
1214

13-
- `object-map` — the object-bound renderer
14-
- `map` — the bare spec view-type name (`ViewTypeSchema`'s `'map'`), for a node
15-
authored with it directly. Inside an `ObjectView`, a `map` view is compiled to
16-
an `object-map` node, so both spellings end at the same component.
15+
> **The bare `map` node type key is retired** (objectui#10393, following the
16+
> objectui#8008 ruling that retired `gantt`). The registry used to accept a node
17+
> authored `"type": "map"` (and its namespaced twin `view:map`) while the
18+
> published declaration refused it — `ObjectMapSchema.type` is the literal
19+
> `'object-map'` and no schema arm names `map` — so a validated document could not
20+
> use the key the registry took. Write `"type": "object-map"`.
21+
>
22+
> ⚠️ The **stored view type** `"map"` — what a saved `listViews[].type` or
23+
> `defaultViewType` holds — is a **different layer and is unchanged**. Do not
24+
> rewrite it: `ObjectView` maps a stored `map` view onto the `object-map` node
25+
> type, so no saved view moves.
1726
1827
## Installation
1928

@@ -88,12 +97,14 @@ is honoured as well.
8897

8998
**The provider does not change which query keys apply** (objectui#9061, the port
9099
of objectui#8769). An authored `filter` and `sort` narrow and order the rows on
91-
**every** provider, inline ones included — `staticData`, a bare array under
92-
`data`, and `data: { provider: 'value', items }` all reach the same in-memory
93-
adapter the other providers go through, so `filter` is evaluated with the same
94-
matcher. Before objectui#9061 the inline provider skipped that query and plotted
95-
every authored row with an authored `filter` silently dropped. The platform row
96-
ceiling (2,000 drawn rows, with a footnote naming both numbers — objectui#7210,
100+
**every** provider, inline ones included — `staticData` and
101+
`data: { provider: 'value', items }` both reach the same in-memory adapter the
102+
other providers go through, so `filter` is evaluated with the same matcher. A
103+
bare array under `data` is not a record source on this map (objectui#8348): the
104+
ladder falls through to `staticData`, then `objectName`, so inline rows belong
105+
under `staticData`. Before objectui#9061 the inline provider skipped that query
106+
and plotted every authored row with an authored `filter` silently dropped. The
107+
platform row ceiling (2,000 drawn rows, with a footnote naming both numbers — objectui#7210,
97108
ruling a′) applies to inline rows too, and it is applied to the **filtered** set,
98109
never to the raw one: a large inline array that a `filter` cuts below the ceiling
99110
plots every matching row and shows no footnote.
@@ -113,7 +124,7 @@ The declared configuration input. Every key is optional:
113124
| `latitudeField` | Record field holding the latitude. Needs `longitudeField` alongside it; both values must be numbers. |
114125
| `longitudeField` | Record field holding the longitude. |
115126
| `locationField` | Single field holding both coordinates — see the formats below. Used when the lat/lng pair yields nothing. |
116-
| `titleField` | Field shown as the marker title. Omitted, the title is resolved by the object's own record-title precedence (`@object-ui/core`'s `getRecordDisplayName`, ADR-0079): the declared `nameField`, its deprecated `displayNameField` alias, the legacy `titleFormat` template, a type-aware pick from the object's fields, then name-ish keys read straight off the record — the rung that answers when no object definition reached the view, as `staticData` and an inline `data` array never fetch one. `Record #<id>` is the floor; `Marker` is reached only by a record carrying no id at all. |
127+
| `titleField` | Field shown as the marker title. Omitted, the title is resolved by the object's own record-title precedence (`@object-ui/core`'s `getRecordDisplayName`, ADR-0079): the declared `nameField`, its deprecated `displayNameField` alias, the legacy `titleFormat` template, a type-aware pick from the object's fields, then name-ish keys read straight off the record — the rung that answers when no object definition reached the view, as `staticData` and an inline `data: { provider: 'value', items }` configuration never fetch one. `Record #<id>` is the floor; `Marker` is reached only by a record carrying no id at all. |
117128
| `descriptionField` | Field shown under the title in the marker popup. |
118129
| `zoom` | Zoom level. Declaring it opts this view out of the auto-fit (see below). |
119130
| `center` | `[latitude, longitude]` — a two-number **tuple**, latitude first. Declaring it opts this view out of the auto-fit. |
@@ -212,7 +223,7 @@ declare const dataSource: ObjectMapProps['dataSource'];
212223
| Prop | Description |
213224
| --- | --- |
214225
| `schema` | The map schema — the keys above. |
215-
| `dataSource` | Resolves the `object` provider. Not needed for `staticData` or an inline `data` array. |
226+
| `dataSource` | Resolves the `object` provider. Not needed for `staticData` or an inline `data: { provider: 'value', items }` configuration. |
216227
| `className` | Classes for the wrapper around the map. |
217228
| `data` | Records to render directly, bypassing the component's own fetch — the shape `ListView` passes when it already holds the rows. Tracked live: passing a new array after mount (e.g. once a host's own in-flight query resolves) updates the map. |
218229
| `onMarkerClick` | Called with the clicked record. |

‎packages/plugin-map/src/ObjectMap.schemaDataShorthand.test.tsx‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ import { describe, it, expect, vi } from 'vitest';
6666
import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react';
6767
import { ComponentRegistry, recordSourceDataArmForType } from '@object-ui/core';
6868
import { ObjectMap } from './ObjectMap';
69-
// Registers `object-map` and its `view:map` alias — row 3 renders through it.
69+
// Registers `object-map` — row 3 renders through it.
7070
import './index';
7171
import type { DataSource } from '@object-ui/types';
7272

@@ -190,15 +190,14 @@ describe('ObjectMap — the bare-array `schema.data` shorthand is retired (objec
190190
// namespaced key and a bare one, and `SchemaRenderer` looks the arm up with
191191
// the raw `schema.type`. A key added without a row in
192192
// `recordSourceDataArmForType` turns this red instead of silently answering
193-
// `'undeclared'` and keeping the prop seat. Unlike `plugin-grid`, this
194-
// plugin claims the bare `map` key too — no `skipFallback` here.
193+
// `'undeclared'` and keeping the prop seat. The bare `map` key and its
194+
// `view:map` twin are retired (objectui#10393), so the group is exactly the
195+
// one registration's two keys.
195196
const siblings = ComponentRegistry.getAllTypes().filter(
196197
(type) => ComponentRegistry.get(type) === ComponentRegistry.get('object-map'),
197198
);
198199

199-
expect(siblings).toEqual(
200-
expect.arrayContaining(['object-map', 'plugin-map:object-map', 'view:map', 'map']),
201-
);
200+
expect([...siblings].sort()).toEqual(['object-map', 'plugin-map:object-map']);
202201
for (const type of siblings) {
203202
expect([type, recordSourceDataArmForType(type)]).toEqual([type, 'view-data']);
204203
}

0 commit comments

Comments
 (0)