diff --git a/.changeset/5157-object-map-config-strict.md b/.changeset/5157-object-map-config-strict.md
new file mode 100644
index 0000000000..5f749ed24a
--- /dev/null
+++ b/.changeset/5157-object-map-config-strict.md
@@ -0,0 +1,36 @@
+---
+'@object-ui/types': minor
+---
+
+feat(types): `ObjectMapConfigSchema` is `.strict()`, so `objectui validate` refuses an undeclared key in an `object-map` node's `map` block (objectui#5157)
+
+⚠️ **The accept set narrows.** `ObjectMapSchema.map` is `ObjectMapConfigSchema`, which used
+to be a plain `z.object()`: a key it did not declare was stripped and the node parsed clean.
+The card's own typo, `latitudeFieId`, therefore validated clean and was never read, and neither
+`objectui validate` nor the runtime named it (only a TypeScript author's compiler did). The block is now closed, as ruled on objectui#5157 (letter A, carrying the earlier
+ruling that limits the change to the `map` block):
+
+- **`safeValidateSchema` / `objectui validate`** refuse a `map` block carrying an undeclared
+ key with an `unrecognized_keys` issue at `map` that names the key, and exit 1. One level
+ down — the node inside a container's `children` — the refusal arrives as an
+ `invalid_union` at `children`, and the arm detail `objectui validate` prints under it
+ names the key at `children → 0 → map`.
+- **`objectui check`** is advisory and only uses validation to recognise a file: a root
+ `object-map` document with no structural root key now moves into its "did not validate"
+ list; a document whose root carries a structural key (`children`, `className`, …) is not
+ validated by `check` and still passes it.
+- **The runtime keeps warning, not throwing.** `ObjectMap` still renders; its existing
+ `safeParse` of the block now fails, so `[ObjectMap] Invalid map configuration` is warned
+ in the console, naming the key.
+- `.shape` is unchanged, so `ObjectMap`'s `FLAT_MAP_CONFIG_KEYS` and the view flatten
+ whitelists see the same keys.
+
+**Measured breakage: none.** The pre-landing sweep the ruling required found no `map` block
+in authored metadata (objectui examples, apps and docs; objectstack examples and apps)
+carrying an undeclared key. Its population is held as a fixture in
+`packages/types/src/__tests__/object-map-config-strict-5157.test.ts`, which re-parses every
+block under the strict schema on each run; the fixture names the commits the population was
+enumerated at, and a block added since is not in it.
+
+Other component sub-block schemas are untouched: whether they close the same way is a
+separate decision.
diff --git a/content/docs/plugins/plugin-map.mdx b/content/docs/plugins/plugin-map.mdx
index 63eeb16538..263f80505a 100644
--- a/content/docs/plugins/plugin-map.mdx
+++ b/content/docs/plugins/plugin-map.mdx
@@ -146,6 +146,15 @@ to `onMarkerClick` is not `===` the authored object.
}
```
+The block is closed (objectui#5157). A key it does not declare — a typo such as
+`latitudeFieId` — is refused by `objectui validate` with an `unrecognized_keys`
+issue at `map` that names the key (on a root node; a nested node reports under
+`invalid_union` at `children`, with the key in the arm detail). At runtime
+`ObjectMap` does not throw: it renders from the declared keys, and the console
+warns `[ObjectMap] Invalid map configuration`, naming the same key. A typo that
+leaves the block with no coordinate binding, as `latitudeFieId` does, draws the
+refusal described under "An unconfigured map refuses" rather than a map.
+
## Configuration
### An unconfigured map refuses
diff --git a/packages/plugin-map/README.md b/packages/plugin-map/README.md
index b55da658f4..397ef3e2c6 100644
--- a/packages/plugin-map/README.md
+++ b/packages/plugin-map/README.md
@@ -117,7 +117,16 @@ round-trip, so inline rows must be JSON-serializable.
## The `map` block
-The declared configuration input. Every key is optional:
+The declared configuration input. Every key is optional, and the block is
+**closed** (objectui#5157): a key outside this table — a typo such as
+`latitudeFieId` — is refused by `objectui validate` with an `unrecognized_keys`
+issue at `map` that names it (on a root node; a nested node reports under
+`invalid_union` at `children`, with the key in the arm detail). At runtime
+`ObjectMap` does not throw: it renders from the declared keys and warns
+`[ObjectMap] Invalid map configuration` in the console, naming the same key; the
+undeclared key is never read. A typo that leaves the block with no coordinate
+binding, as `latitudeFieId` does, therefore draws the "Map configuration
+required" refusal below rather than a map.
| Key | Description |
| --- | --- |
diff --git a/packages/plugin-map/src/ObjectMap.strictConfigWarn-5157.test.tsx b/packages/plugin-map/src/ObjectMap.strictConfigWarn-5157.test.tsx
new file mode 100644
index 0000000000..791ae59e93
--- /dev/null
+++ b/packages/plugin-map/src/ObjectMap.strictConfigWarn-5157.test.tsx
@@ -0,0 +1,95 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * objectui#5157 — the RUNTIME face of `ObjectMapConfigSchema.strict()`.
+ *
+ * Ruled letter A on objectui#5157: the schema closes, so `safeValidateSchema`
+ * refuses an undeclared `map` key (pinned in `@object-ui/types`'
+ * `object-map-config-strict-5157.test.ts`), while the component itself keeps
+ * the channel it already had — it RENDERS, and `getMapConfig`'s `safeParse`
+ * of the block now fails and `console.warn`s the issue. Warn, not throw: a
+ * document the validator refuses still draws on a running page.
+ *
+ * Before the strict schema the same block parsed clean with the key stripped,
+ * so this warning never fired: the card's symptom was a quiet console: no
+ * warning named the key. The pin therefore reads the warning's ARGUMENTS, not its first line —
+ * the key arrives in the formatted issue passed after the prefix string.
+ */
+
+import React from 'react';
+import { render, screen, waitFor } from '@testing-library/react';
+import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
+import type { ObjectMapSchema } from '@object-ui/types';
+import { ObjectMap } from './ObjectMap';
+
+type Slot = { children?: React.ReactNode };
+
+vi.mock('react-map-gl/maplibre', () => ({
+ default: ({ children }: Slot) =>
{children}
,
+ Map: ({ children }: Slot) => {children}
,
+ NavigationControl: () => ,
+ Marker: ({ children, longitude, latitude }: Slot & { longitude: number; latitude: number }) => (
+
+ {children}
+
+ ),
+ Popup: ({ children }: Slot) => {children}
,
+}));
+
+/** The card's own typo: `latitudeFieId`, capital i where the l belongs. */
+const TYPO_KEY = 'latitudeFieId';
+
+const ROWS = [{ id: '1', name: 'Ferry Building', location: { lat: 37.7955, lng: -122.3937 } }];
+
+let warnSpy: ReturnType;
+
+beforeEach(() => {
+ warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
+});
+
+afterEach(() => {
+ warnSpy.mockRestore();
+});
+
+/** Every argument of every `[ObjectMap] Invalid map configuration` call, serialised. */
+const invalidConfigWarnings = () =>
+ warnSpy.mock.calls
+ .filter((args: unknown[]) => String(args[0]).includes('[ObjectMap] Invalid map configuration'))
+ .map((args: unknown[]) => args.map((a) => (typeof a === 'string' ? a : JSON.stringify(a))).join(' '));
+
+const renderMap = async (map: Record) => {
+ const schema = { type: 'object-map', staticData: ROWS, map } as unknown as ObjectMapSchema;
+ const utils = render();
+ await waitFor(() => expect(screen.queryByText('Loading map...')).toBeNull());
+ return utils;
+};
+
+describe('(i) an undeclared `map` key still renders, and the warning names it (objectui#5157)', () => {
+ it('renders the map and places the marker the declared keys bind', async () => {
+ await renderMap({ locationField: 'location', titleField: 'name', [TYPO_KEY]: 'lat' });
+
+ expect(screen.getByLabelText('Map')).toBeTruthy();
+ const markers = screen.getAllByTestId('map-marker');
+ expect(markers).toHaveLength(1);
+ expect(markers[0]).toHaveAttribute('data-lat', '37.7955');
+ });
+
+ it('warns through the existing channel, naming the undeclared key', async () => {
+ await renderMap({ locationField: 'location', titleField: 'name', [TYPO_KEY]: 'lat' });
+
+ const warned = invalidConfigWarnings();
+ expect(warned.length).toBeGreaterThan(0);
+ expect(warned.join('\n')).toContain(TYPO_KEY);
+ });
+
+ it('says nothing about the configuration when every key is declared (control)', async () => {
+ await renderMap({ locationField: 'location', titleField: 'name' });
+
+ expect(screen.getAllByTestId('map-marker')).toHaveLength(1);
+ expect(invalidConfigWarnings()).toEqual([]);
+ });
+});
diff --git a/packages/types/README.md b/packages/types/README.md
index 0468add616..56de4617af 100644
--- a/packages/types/README.md
+++ b/packages/types/README.md
@@ -127,7 +127,9 @@ type ButtonSchema = SchemaByType<'button'>;
- The **rendering face** (`AnyComponentSchema`, `SchemaNodeSchema`, every named
mirror) is tolerant: a node may carry keys the schema does not declare, because
- renderer props ride through it.
+ renderer props ride through it. Some declared sub-blocks are closed on this face
+ as well — an `object-map` node's `map` block is one (objectui#5157) — so a key
+ misspelled inside one of them is refused by both faces.
- The **strict authoring face** is a derived twin that closes every declared
object, at every depth. It is meant for authoring-time checking — validating a
document a person or an agent just wrote — where an undeclared key is far more
diff --git a/packages/types/src/__tests__/object-map-config-strict-5157.test.ts b/packages/types/src/__tests__/object-map-config-strict-5157.test.ts
new file mode 100644
index 0000000000..8c665e2a83
--- /dev/null
+++ b/packages/types/src/__tests__/object-map-config-strict-5157.test.ts
@@ -0,0 +1,186 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * objectui#5157 — `ObjectMapConfigSchema` is `.strict()`, so an `object-map`
+ * node whose `map` block carries an undeclared key is REFUSED on the validate
+ * face, by name, instead of parsing clean with the key stripped.
+ *
+ * Ruled letter A on objectui#5157 (carrying the earlier ruling that limits the
+ * change to the `map` block): `.strict()` goes on the schema itself, so the
+ * declared face, the runtime check `ObjectMap` performs and the validate face
+ * keep ONE accept set. The runtime half (the component still renders and warns,
+ * naming the key) is pinned beside the renderer, in `plugin-map`'s
+ * `ObjectMap.strictConfigWarn-5157.test.tsx`.
+ *
+ * Three things are pinned here:
+ *
+ * (ii) the validate face — `safeValidateSchema` refuses a `map` block carrying
+ * the card's typo with ONE `unrecognized_keys` issue at `map`, naming the
+ * key, and still accepts the clean block;
+ * (iii) the pre-landing sweep, held rather than remembered — every `map`
+ * block the sweep found in authored metadata parses clean under strict;
+ * and the boundary: `.shape` did not move, so every key list read from it
+ * sees the same keys.
+ */
+
+import { describe, it, expect } from 'vitest';
+import { ObjectMapConfigSchema, ObjectMapSchema } from '../zod/objectql.zod.js';
+import { safeValidateSchema } from '../zod/index.zod.js';
+
+/** The card's own typo: `latitudeFieId`, capital i where the l belongs. */
+const TYPO_KEY = 'latitudeFieId';
+
+const CLEAN_NODE = {
+ type: 'object-map',
+ objectName: 'stores',
+ map: { latitudeField: 'lat', longitudeField: 'lng' },
+};
+
+const TYPO_NODE = {
+ type: 'object-map',
+ objectName: 'stores',
+ map: { [TYPO_KEY]: 'lat', longitudeField: 'lng' },
+};
+
+/** The same node, one level down: what a page or a view actually carries. */
+const NESTED_TYPO = { type: 'div', children: [TYPO_NODE] };
+const NESTED_CLEAN = { type: 'div', children: [CLEAN_NODE] };
+
+/** Each issue reduced to what a consumer keys off: its kind, its place, the keys it names. */
+function issues(result: { success: boolean; error?: { issues: ReadonlyArray<{ code: string; path: PropertyKey[]; keys?: string[] }> } }) {
+ return (result.error?.issues ?? []).map((i) => ({ code: i.code, path: i.path, keys: i.keys }));
+}
+
+describe('(ii) the validate face refuses an undeclared `map` key, by name (objectui#5157)', () => {
+ it('refuses the typo block with one `unrecognized_keys` issue at `map`, naming the key', () => {
+ const result = safeValidateSchema(TYPO_NODE);
+ expect(result.success).toBe(false);
+ expect(issues(result)).toEqual([{ code: 'unrecognized_keys', path: ['map'], keys: [TYPO_KEY] }]);
+ });
+
+ it('refuses the typo block on the node schema itself, at the same path', () => {
+ const result = ObjectMapSchema.safeParse(TYPO_NODE);
+ expect(result.success).toBe(false);
+ expect(issues(result)).toEqual([{ code: 'unrecognized_keys', path: ['map'], keys: [TYPO_KEY] }]);
+ });
+
+ it('refuses the typo block on the config schema, naming the key at the block root', () => {
+ const result = ObjectMapConfigSchema.safeParse(TYPO_NODE.map);
+ expect(result.success).toBe(false);
+ expect(issues(result)).toEqual([{ code: 'unrecognized_keys', path: [], keys: [TYPO_KEY] }]);
+ });
+
+ it('refuses the typo block one level down, inside a container', () => {
+ // The nested refusal arrives as the child slot's union issue, not as a
+ // named key: that is how the undiscriminated `children` union reports.
+ // Only the verdict is pinned here; how `objectui validate` renders it is
+ // measured on the pull request, not pinned.
+ expect(safeValidateSchema(NESTED_TYPO).success).toBe(false);
+ });
+
+ it('still accepts the clean block, alone and nested (control)', () => {
+ expect(issues(safeValidateSchema(CLEAN_NODE))).toEqual([]);
+ expect(safeValidateSchema(CLEAN_NODE).success).toBe(true);
+ expect(safeValidateSchema(NESTED_CLEAN).success).toBe(true);
+ });
+});
+
+describe('`.strict()` closes the block without moving its shape (objectui#5157)', () => {
+ it('declares the same eight keys, so every key list read from `.shape` is unchanged', () => {
+ // `ObjectMap`'s `FLAT_MAP_CONFIG_KEYS` is this list minus `style`; the view
+ // flatten whitelists are pinned against it in their own packages.
+ expect(Object.keys(ObjectMapConfigSchema.shape).sort()).toEqual([
+ 'center',
+ 'descriptionField',
+ 'latitudeField',
+ 'locationField',
+ 'longitudeField',
+ 'style',
+ 'titleField',
+ 'zoom',
+ ]);
+ });
+
+ it('accepts every declared key together', () => {
+ const everything = {
+ latitudeField: 'lat',
+ longitudeField: 'lng',
+ locationField: 'location',
+ titleField: 'name',
+ descriptionField: 'address',
+ zoom: 12,
+ center: [37.7749, -122.4194],
+ style: 'https://tiles.example.com/style.json',
+ };
+ expect(issues(ObjectMapConfigSchema.safeParse(everything))).toEqual([]);
+ });
+});
+
+/**
+ * (iii) The pre-landing sweep the first ruling on objectui#5157 required, held
+ * as a fixture so its verdict is re-derived on every run instead of quoted.
+ *
+ * ⚠️ The POPULATION is a snapshot, and nothing here re-enumerates it: it is
+ * every `map` block the sweep found in authored metadata, enumerated with
+ * `git ls-tree` and a `git grep` for a `map` key over objectui `examples` /
+ * `apps` / `content` at `40c076fc2d` and objectstack `examples` / `apps` at
+ * `3cf6449389`, with every grep hit reconciled by hand (translation labels,
+ * view names and catalog entry ids keyed `map` are not config blocks). A block
+ * added after those commits is NOT in this list. The three catalog entries are
+ * also re-read live from disk and validated whole by `plugin-map`'s
+ * `ObjectMap.catalogRecordSource-6939.test.tsx`.
+ *
+ * Each row names its source by path and a quoted anchor — ⛔ never a line
+ * address (AGENTS.md #11).
+ */
+const SWEEP: ReadonlyArray<{ repo: 'objectui' | 'objectstack'; path: string; anchor: string; block: Record }> = [
+ { repo: 'objectui', path: 'content/docs/plugins/plugin-map.mdx', anchor: '### Basic Usage with ObjectQL', block: { latitudeField: 'lat', longitudeField: 'lng', titleField: 'name', descriptionField: 'address' } },
+ { repo: 'objectui', path: 'content/docs/plugins/plugin-map.mdx', anchor: '### With Static Data', block: { latitudeField: 'lat', longitudeField: 'lng', titleField: 'name', descriptionField: 'address' } },
+ { repo: 'objectui', path: 'content/docs/plugins/plugin-map.mdx', anchor: 'const storeMap', block: { latitudeField: 'latitude', longitudeField: 'longitude', titleField: 'storeName', descriptionField: 'storeAddress' } },
+ { repo: 'objectui', path: 'content/docs/plugins/plugin-map.mdx', anchor: 'const placeMap', block: { locationField: 'coordinates', titleField: 'placeName', descriptionField: 'description' } },
+ { repo: 'objectui', path: 'content/docs/plugins/plugin-map.mdx', anchor: 'const cameraMap', block: { latitudeField: 'lat', longitudeField: 'lng', titleField: 'name', zoom: 12, center: [37.7749, -122.4194] } },
+ { repo: 'objectui', path: 'content/docs/plugins/plugin-map.mdx', anchor: 'const retailStores', block: { latitudeField: 'store_lat', longitudeField: 'store_lng', titleField: 'store_name', descriptionField: 'store_address' } },
+ { repo: 'objectui', path: 'content/docs/plugins/plugin-map.mdx', anchor: 'const staticLocations', block: { latitudeField: 'lat', longitudeField: 'lng', titleField: 'name' } },
+ { repo: 'objectui', path: 'content/docs/plugins/plugin-map.mdx', anchor: 'const clickableMap', block: { latitudeField: 'lat', longitudeField: 'lng', titleField: 'name' } },
+ { repo: 'objectui', path: 'content/docs/plugins/plugin-map.mdx', anchor: 'const storeLocator', block: { latitudeField: 'latitude', longitudeField: 'longitude', titleField: 'storeName', descriptionField: 'fullAddress', zoom: 10, center: [37.7749, -122.4194] } },
+ { repo: 'objectui', path: 'content/docs/plugins/plugin-map.mdx', anchor: 'const deliveryMap', block: { latitudeField: 'current_lat', longitudeField: 'current_lng', titleField: 'driver_name', descriptionField: 'delivery_address' } },
+ { repo: 'objectui', path: 'content/docs/plugins/plugin-map.mdx', anchor: 'const propertyMap', block: { latitudeField: 'property_lat', longitudeField: 'property_lng', titleField: 'property_address', descriptionField: 'property_details', zoom: 12 } },
+ { repo: 'objectui', path: 'content/docs/plugins/plugin-map.mdx', anchor: 'const venueMap', block: { latitudeField: 'lat', longitudeField: 'lng', titleField: 'venueName', descriptionField: 'details', zoom: 11 } },
+ { repo: 'objectui', path: 'content/docs/plugins/plugin-map.mdx', anchor: 'const serviceMap', block: { latitudeField: 'customer_latitude', longitudeField: 'customer_longitude', titleField: 'customer_name', descriptionField: 'service_type', zoom: 10 } },
+ { repo: 'objectui', path: 'content/docs/plugins/plugin-map.mdx', anchor: 'const mapConfig: ObjectMapConfig', block: { latitudeField: 'lat', longitudeField: 'lng', titleField: 'name', descriptionField: 'description', zoom: 12, center: [37.7749, -122.4194] } },
+ { repo: 'objectui', path: 'content/docs/fields/location.mdx', anchor: '**2. The map over the same field.**', block: { locationField: 'location', titleField: 'name', zoom: 12, center: [37.7749, -122.4194] } },
+ { repo: 'objectui', path: 'examples/schema-catalog/src/schemas/plugin-map/event-venue-finder.json', anchor: '"map"', block: { latitudeField: 'latitude', longitudeField: 'longitude', titleField: 'venue', descriptionField: 'info', zoom: 11 } },
+ { repo: 'objectui', path: 'examples/schema-catalog/src/schemas/plugin-map/real-time-delivery-tracking.json', anchor: '"map"', block: { latitudeField: 'current_lat', longitudeField: 'current_lng', titleField: 'driver', descriptionField: 'destination', zoom: 12 } },
+ { repo: 'objectui', path: 'examples/schema-catalog/src/schemas/plugin-map/store-locator-map.json', anchor: '"map"', block: { latitudeField: 'lat', longitudeField: 'lng', titleField: 'name', descriptionField: 'address', zoom: 9 } },
+ { repo: 'objectstack', path: 'examples/app-showcase/src/ui/views/task.view.ts', anchor: "label: 'Work Locations (Map)'", block: { titleField: 'title', locationField: 'location' } },
+ { repo: 'objectstack', path: 'examples/app-showcase/test/task-map-marker-title.test.ts', anchor: "name: 'showcase_task_map_direct_probe'", block: { titleField: 'title', locationField: 'location' } },
+];
+
+describe('(iii) every `map` block the pre-landing sweep found parses clean under strict (objectui#5157)', () => {
+ it('holds the swept population, split as the sweep found it', () => {
+ // Non-vacuity, not a live count: the sweep's own population at the two
+ // commits named above. A row removed here without a reason turns this red.
+ expect(SWEEP.filter((row) => row.repo === 'objectui')).toHaveLength(18);
+ expect(SWEEP.filter((row) => row.repo === 'objectstack')).toHaveLength(2);
+ });
+
+ it.each(SWEEP.map((row) => [`${row.repo} ${row.path} (${row.anchor})`, row.block] as const))(
+ '%s parses clean as a map block',
+ (_label, block) => {
+ expect(issues(ObjectMapConfigSchema.safeParse(block))).toEqual([]);
+ },
+ );
+
+ it.each(SWEEP.map((row) => [`${row.repo} ${row.path} (${row.anchor})`, row.block] as const))(
+ '%s validates as the `map` block of an object-map node',
+ (_label, block) => {
+ expect(issues(safeValidateSchema({ type: 'object-map', objectName: 'stores', map: block }))).toEqual([]);
+ },
+ );
+});
diff --git a/packages/types/src/zod/objectql.zod.ts b/packages/types/src/zod/objectql.zod.ts
index e2c5617349..d1b031dd3a 100644
--- a/packages/types/src/zod/objectql.zod.ts
+++ b/packages/types/src/zod/objectql.zod.ts
@@ -1865,6 +1865,29 @@ export type ListViewInferred = z.input;
* so the declared authoring face and the validation the renderer performs are
* ONE schema rather than two that can drift (objectui#5018). `ObjectMap`
* imports this exact object; it no longer declares its own.
+ *
+ * ## `.strict()` — an undeclared key is REFUSED, not stripped (objectui#5157)
+ *
+ * The TypeScript twin `ObjectMapConfig` is a closed interface, so a misspelled
+ * key (`latitudeFieId`) was a compile error for a typed author and nothing at
+ * all for untyped metadata: this object stripped it and parsed clean, and
+ * nothing named it: the card's typo drew the generic "Map configuration
+ * required" refusal (objectui#8169), which names the key the author meant, not
+ * the one they wrote. Closing the block makes all three faces
+ * one accept set, as ruled on objectui#5157 (letter A, carrying the earlier
+ * "the `map` block only" ruling):
+ *
+ * - runtime: `ObjectMap`'s `safeParse` of the block now fails, so the
+ * component still renders and `console.warn`s the issue, which names the key;
+ * - validate: `ObjectMapSchema.map` is this object, so `safeValidateSchema`
+ * (and `objectui validate` with it) refuses the node with an
+ * `unrecognized_keys` issue at `map`;
+ * - `.shape` is untouched, so `ObjectMap`'s `FLAT_MAP_CONFIG_KEYS` (derived
+ * from it) and the view flatten whitelists (hand-listed, pinned against it)
+ * see the same keys as before.
+ *
+ * ⛔ The map block ONLY. Whether other component sub-block schemas close the
+ * same way is a separate decision the ruling kept out of this card.
*/
export const ObjectMapConfigSchema = z.object({
latitudeField: z.string().optional().describe('Field containing latitude'),
@@ -1875,7 +1898,7 @@ export const ObjectMapConfigSchema = z.object({
zoom: z.number().optional().describe('Zoom level (1-20); declaring it opts out of the auto-fit'),
center: z.tuple([z.number(), z.number()]).optional().describe('Center [lat, lng]; declaring it opts out of the auto-fit'),
style: z.string().optional().describe('MapLibre style URL/spec (overrides the public demo default)'),
-});
+}).strict();
/**
* `77cb489b4` — the record-source refinement `ObjectMapSchema`,
diff --git a/scripts/markdown-test-inputs.mjs b/scripts/markdown-test-inputs.mjs
index 2812b0f535..2d4638741c 100644
--- a/scripts/markdown-test-inputs.mjs
+++ b/scripts/markdown-test-inputs.mjs
@@ -476,6 +476,18 @@ export const ADJUDICATED = new Map([
'packages/types/src/__tests__/object-kanban-group-by-limit-7322.test.ts',
{ reads: ['content/docs/plugins/plugin-kanban.mdx'] },
],
+ // objectui#5157. Reads no markdown: the two document literals are PROVENANCE
+ // on the rows of its sweep fixture (every `map` block the pre-landing sweep
+ // found in authored metadata, copied into the test and parsed under the strict
+ // schema). The test imports no `fs` and opens neither page, so an edit to
+ // either one cannot move its verdict.
+ [
+ 'packages/types/src/__tests__/object-map-config-strict-5157.test.ts',
+ {
+ reads: [],
+ notRead: ['content/docs/fields/location.mdx', 'content/docs/plugins/plugin-map.mdx'],
+ },
+ ],
[
'packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts',
{