Repository navigation
fix(plugin-list): resolve context tokens in a directly authored object-gallery filter (objectui#10666) - #10705
Conversation
…d object-gallery filter (objectui#10666)
RED against the unfixed ObjectGallery: the node sends `{current_user_id}`
and `{current_org_id}` literally on `$filter`. The list-view cell in the
same harness is the lit control and passes.
Co-Authored-By: Claude <noreply@anthropic.com>
…t-gallery filter (objectui#10666)
ObjectGallery sent its own `filter` on `$filter` with `{current_user_id}`
left literal. It now resolves the filter once through core's shared
`resolveFilterPlaceholders` against `useFilterScope()`, held by structure
with the same hook ListView has used since objectui#10607. That hook moves
unchanged from ListView.tsx into a package-internal module so both nodes
share it; the package export set does not move.
Co-Authored-By: Claude <noreply@anthropic.com>
|
changeset-claim-re-read
|
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
✅ ACCEPTED — PR objectui#10705 at
|
Part of #10666
Clause-②: no. The documented context tokens now resolve on one more host, through the one shared resolver. No schema, export or accept set moves.
This PR fixes the node the card names,
object-gallery. The census the triage ordered found eleven more nodes that send their own filter unresolved. The seat decides how to close them (see Census and The fork), so this PR usesPart ofand does not close the card.What changed
A directly authored
object-gallerynode, with nolist-vieworobject-viewabove it, sentfilter: [['owner', '=', '{current_user_id}']]on$filteras the literal token.ObjectGallerynow resolves its own filter once, through@object-ui/core's sharedresolveFilterPlaceholders, againstuseFilterScope()from@object-ui/react. The resolved value is held by structure, in the shape PR objectui#10656 gavelist-view.useResolvedAuthoredFilterwith its comparatorisSameAuthoredFilter. PR objectui#10656 added it as a function local toListView.tsx. It now moves, unchanged, into the package-internal modulepackages/plugin-list/src/useResolvedAuthoredFilter.ts.ListViewandObjectGalleryboth import it.index.tsxdoes not re-export it, so the package's export set does not move.ObjectGallery. The query's$filterand the fetch effect's dependency list now read the heldauthoredFilter. After this change the only rawschema.filterread is the hold's own input..changeset/10666-gallery-filter-context-tokens.md,'@object-ui/plugin-list': patch.packages/plugin-list/src/ObjectGallery.tsxpackages/plugin-list/src/useResolvedAuthoredFilter.tsListView.tsxpackages/plugin-list/src/ListView.tsxpackages/plugin-list/src/__tests__/ObjectGallery.filterContextTokens-10666.test.tsx.changeset/10666-gallery-filter-context-tokens.mdMeasured
The measurement is the pin's first run, on the unfixed code (commit
8f38562, pins only). The node was mounted through the realSchemaRendererand this package's own registrations, underFilterScopeProvider(userusr_42, orgorg_7), over a data source that records everyfind().$filtersent on baselist-view(lit control)[["owner","=","usr_42"]]: resolved, the cell passesobject-gallery[["owner","=","{current_user_id}"]]: the literal tokenobject-gallerywith{current_org_id}[["org","=","{current_org_id}"]]: the literal tokenThe pin has 8 cells. It checks the
list-viewlit control,{current_user_id}and{current_org_id}, and a token-free CONTROL. It checks that with no scope the token stays literal and the shared resolver warns (no fallback). It checks that a user change re-queries. It checks that the same node issues exactly one query across three re-renders. It checks that no query is added across re-renders that rebuild an equal token filter.8f38562(pins only, base code)expected [ [ 'org', '=', '{current_org_id}' ] ] to deeply equal [ [ 'org', '=', 'org_7' ] ]). The cells that pass are the lit control, the CONTROL and the same-node count.59d06fcThe hold, by ablation.
ablation-replace.mjsreplaced the hook'sreturn held.resolved as T;with a bare per-renderresolveFilterPlaceholders(authored, scope)return. The anchor hit once, and the blob went from9d7fa15to62871c1. The result was 3 failed and 5 passed. The same-node cell readsexpected 0 to be greater than 0: nofind()landed inside the 1000ms wait while the gallery kept re-rendering. The user-change and equal-rebuild cells time out at 15000ms, because a copy minted every render re-queries without end. The token cells stay green. The restore was checked: the blob after restore equals the blob at HEAD, andgit diff HEADis empty. The pin imports the subject through../index(fromsrc/), so there is nodist/leg.Suites and gates at
59d06fc. Heavy runs went through the shared verify lock.turbo run build --filter='@object-ui/plugin-list^...' --concurrency=2: 12 tasks successful.plugin-listtype-check(tsc --noEmit && tsc -p tsconfig.test.json): exit 0.--listFilesOnlylists the new pin (1 hit).pnpm exec vitest run packages/plugin-list/: 102 files, 1169 tests passed, exit 0.pnpm exec vitest run packages/i18n/src/__tests__/machineLocaleCensus-9909.test.ts: 7 passed.check:changeset-claims,check:pending-changeset-literals,check:new-line-citations(0 new),check:control-bytes,check-changeset-presence(4 source files of 1 released package, 1 changeset),check-changeset-no-major,check-changeset-overwrite,check-changeset-fixed,check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shape,check:test-path-roots,check:unreferenced-sources,check:phantom-deps,check:self-importandcheck-type-check-coverage.check:changeset-claimsis report-only. It lists 7 pending paragraphs that nameListView.tsx. Each was re-read, and none concerns the filter read. They cover the gantt branch, kanbangroupFieldandgroupBy,userActions, and comment corrections.69a6fc1against head, are unchanged:ObjectGallery.tsx17 → 17 andListView.tsx184 → 184, with 0 rules differing. The new module and the pin have 0 findings.NOT MEASURED:
check:eager-closureandcheck:sdui-registration-pins. Both need a console production build. The only new import isuseFilterScopefrom@object-ui/react, which the package already loads. Repo-widepnpm lintand the full test farm belong to CI.Census: nodes that send their OWN filter into a data-source query
Read on
origin/main69a6fc1, inplugin-*andcomponents, over every source that callsfind/aggregateor builds a$filter. "Reachable" means the node type is registered and the filter comes from its own schema or props. For the unresolved rows this is a source reading. Onlyobject-galleryand thelist-viewcontrol were measured.object-gridplugin-grid/src/ObjectGrid.tsxlist-viewplugin-list/src/ListView.tsxobject-viewplugin-view/src/ObjectView.tsxobject-chartplugin-charts/src/ObjectChart.tsxplugin-dashboard/src/{DatasetWidget,ObjectMetricWidget,ObjectPivotTable,ObjectDataTable}.tsxobject-galleryplugin-list/src/ObjectGallery.tsxobject-calendarplugin-calendar/src/ObjectCalendar.tsx$filter: schema.filter(object and inline paths)object-ganttplugin-gantt/src/ObjectGantt.tsx$filter: schema.filterobject-kanbanplugin-kanban/src/ObjectKanban.tsx$filter: schema.filterobject-mapplugin-map/src/ObjectMap.tsx$filter: schema.filter(object and inline paths)object-timelineplugin-timeline/src/ObjectTimeline.tsx$filter: schema.filter(already keyed by content,filterKey)object-treeplugin-tree/src/ObjectTree.tsx$filter: schema.filter(object and inline paths)related_list/related-listplugin-detail/src/RelatedList.tsxline_itemsplugin-form/src/LineItemsPanel.tsxschema.filteris merged into the parent scope unresolvedrepeatercomponents/src/renderers/basic/data-list.tsxquery.$filter = props.filternumber(element)components/src/renderers/basic/elements.tsxaggregatefilter and thefindfallback's$filterrecord_pickercomponents/src/renderers/basic/record-picker.tsxcomposed?.filter ?? props.filtercomponents/src/renderers/layout/containers.tsxfilterfor a countFilters that are not authored were left out: parent-scope and
$inid lookups inplugin-detail, the history and activity feeds, the reference rail, and the filter builder's option lookups.plugin-report'sDatasetReportRendererrefuses a node-levelfilter, so it is not a hit. No hit was found outside the renderer packages: theapp-shellcallers (views/ObjectView.tsx,ObjectDataPage.tsx,useOpenRecordList.ts) all resolve.The fork (for the seat, not decided here)
Eleven unresolved nodes remain, in nine packages:
plugin-calendar,plugin-gantt,plugin-kanban,plugin-map,plugin-timeline,plugin-tree,plugin-detail,plugin-formandcomponents. In the ruled shape, each package would need its own copy of the structural hold, since no shared hold is exported. That means nine copies of the ~90-line hook and comparator. PR objectui#10656 accepted two copies on the grounds that "a shared export would move an export set", and the dispatch lists a new export as a STOP condition. The options:useResolvedFilterhold from@object-ui/react, next touseFilterScope, then sweep. There is one copy, but it is a new export (Clause-② yes). It needs a decision.Acceptance notes
{today}and the rest) in a directly authored gallery's filter are now resolved on the client in the browser's local time, the same aslist-view,object-gridandobject-view. Before, they reached the server as literals, which resolves them in the tenant's time zone.{current_user_id}for a signed-in request and refuses the request otherwise. There, this is defence in depth.ObjectGallerySchema.filterdocblock in@object-ui/typessays the filter is "forwarded verbatim as$filter". That is now true only for a filter with no placeholders;ObjectChartSchemacarries the same wording and resolves as well. It is not edited here, because it is out of this PR's file surface.ObjectGridandListViewstay green (19 passed, in the same run as this pin).The session behind this change is
https://claude.ai/code/session_01D486C1axmXnrkJMNUfz2eb(domain:ui seat 3, dispatched dev).Generated by Claude Code