Uh oh!
There was an error while loading. Please reload this page.
fix(plugin-map): read map config from the declared map input only, not schema.filter - #4331
Merged
Merged
Conversation
…not `schema.filter` (#4034) `getMapConfig` probed every filter for a `map` key and used the hit as the MapConfig (`schema.filter.map`, plus a `schema.filter.map.style` half in the style chain) — a shape predating the `{ name: 'map', type: 'object' }` input both registrations declare, and a second meaning for `filter` inside a block that also sends it to the query as `$filter`. The probe was `'map' in schema.filter`, and `in` walks the prototype chain. The ordinary filter is an array, every array inherits `Array.prototype.map`, so the probe matched and handed the component a function as its config; the spread of a function is `{}`, and the declared `schema.map` sat unreachable in the `else` branch. A map authored with both `map` and `filter` — two declared inputs, no legacy shape needed — therefore lost its field mapping and rendered zero markers. Same for the `and` node objectstack#7121's dataSource binding merges, which is likewise an array. Both legacy reads are deleted. The declared `map` config, the top-level `locationField`/`latitudeField` branch and the `style`/`mapStyle` reads are untouched, and `filter` reaches the query verbatim. A schema still carrying the legacy stash gets a dev-mode warning naming the shape and pointing at `schema.map`, following the warn-once diagnostic idiom of `metadata-admin/predicate.ts` (#4049): own-property and object-valued only, so an inherited `map` method or a field genuinely named `map` never trips it. Source thread: objectstack-ai/objectstack#7138. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
marked this pull request as ready for review
August 11, 2026 15:36
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#4034. Source thread: objectstack-ai/objectstack#7138 (triage promotion of record; its anchors re-verified below at current
main).getMapConfigprobed every filter for amapkey and, on a hit, used it as the map's configuration —schema.filter.map, plus aschema.filter.map.stylehalf in the style chain. That shape predates the{ name: 'map', type: 'object' }input both registrations declare, and it gavefiltertwo meanings inside one block: the query filter at$filter: schema.filter, and a configuration slot.Both reads are deleted. The block consumes only what it declares.
Dispatch guard — grep first, 0 live usages
Run before deleting anything, per the triage ruling. Patterns, across this repo (including
examples/and the schema-catalog fixtures) and the siblingobjectstackcheckout:filter\s*:\s*\{[^{}]{0,400}\bmap\s*:(multiline)"filter"\s*:\s*\{[^{}]{0,400}"map"\s*:(JSON)filter.map/filter?.mapArray.prototype.mapcalls, one commentfilter:block with nestedmap:(YAML)All three
object-mapcatalog fixtures (examples/schema-catalog/src/schemas/plugin-map/) author the canonicalmap. The docs (content/docs/plugins/plugin-map.mdx, package README) documentmaponly and never mentioned the legacy shape, so nothing in them became false. The twoobjectstackhits for the string "object-map" are the phrase used to mean a JS object-as-map (fieldsshape) inpackages/restexport tests, not this block.The premise is confirmed, and it is larger than the card recorded
Both anchors were live at
b7da03dd7: the style half atObjectMap.tsx:145, the config probe at:163-164.The card and triage both describe this as observation-class — "no user hits it today; it needs an undocumented legacy shape". That is true of the
filter.mapstash, but the probe has a second, live effect that neither had measured:inwalks the prototype chain, and the ordinary filter is an array. Every array inheritsArray.prototype.map, so the probe matched on any array-shaped filter and assigned the inherited function as the MapConfig. The spread of a function is{}, and the declaredschema.mapsat unreachable in theelsebranch. Measured:So a map authored with both
mapandfilter— two declared inputs, no legacy shape required — lostlatitudeField/longitudeField/titleField, failedextractCoordinateson every record, and rendered zero markers behind a "records with missing or invalid coordinates excluded from the map" banner. The only console output was[ObjectMap] Invalid map configuration:from the Zod parse of a function.This also corrects the mechanism the card predicted for the objectstack#7121 binding path. The card expected the merged filter to carry no
mapkey and the map to fall back to the default config. Measured, the merged node is an array:so the probe hit
Array.prototype.mapthere too — the result was not the default config but an empty one. Same outcome for the user (no markers), different cause; the test pins the real shape.What changed
:163-164) and thefilter.map.stylehalf (:145).schema.mapconfig, the top-levellocationField/latitudeFieldbranch, and thestyle/mapStyletop-level reads.filterkeeps exactly one meaning in the file:$filter: schema.filteris untouched, and the filter is not rewritten — a field genuinely namedmapstill filters on it.schema.map.The warning follows the existing warn-once diagnostic idiom of
app-shell/src/views/metadata-admin/predicate.ts(#4049) rather than a new logging dialect: defensiveglobalThis.process?.env?.NODE_ENVdev gate (the formplugin-dashboarduses, sinceprocessmay not exist in a pure browser bundle), a[ObjectMap]prefix matching this file's existing warn, and a warn-once memo —getMapConfigruns on every render, and a flooding warning is a muted one. It is deliberately narrow: own properties only, so an inheritedmapmethod never trips it, and object-valued only, sofilter: { map: 'x' }reads as a filter on a field namedmap.Production behavior is unchanged beyond the configuration no longer being read.
Tests
New
packages/plugin-map/src/ObjectMap.filterConfig.test.tsx, written red-first with the direction predicted before each run.Reverse verification by restoring
ObjectMap.tsxtoorigin/mainwith the new tests kept (commit-then-revert, nevergit stash) — prediction was "the 7 behavioral tests go red, the 6 controls stay green", and that is exactly what happened, identical to the pre-implementation baseline:schema.mapfilter.map.stylehalf no longer readschema.mapapplies, readsmap.style, no warninglatitudeFieldbranch$filterreceives array / object /map-keyed filters verbatimmaprenders markers (the live defect)mapkey)andbinding node keeps the config, no false-positive warningThe merged-node test pins the mechanism so it cannot come back: the node has no own
mapkey, yet'map' init is true.Commands:
Downstream consumer sweep — only
apps/consoleandapps/sitedepend on@object-ui/plugin-map; console'spublic-contractandpublic-block-binding-reachsuites pass (27 tests). Dependency closure was built before type-checking.Changeset:
patchfor@object-ui/plugin-map.Generated by Claude Code