Skip to content

[objectui] four live adapter.find calls pass an unprefixed query option — and no-query-params-under-options gates only the sibling half of the class #10470

Closed as not planned

Description

@os-zhuang

Filed, not fixed: the sites are in objectstack-ai/objectui, outside #10288's file
surface. Census taken at objectui c40f3b8ca21ddc19e05682f1719fcf16d4ce7fba
(objectstack pins 9a3daf8d37ad973a621e5edd276fe32467f90684; re-confirm at the pin
before acting).

The class

QueryParams (packages/types/src/data.ts) declares only $-prefixed keys, and
ObjectStackAdapter.convertQueryParams (packages/data-objectstack/src/index.ts)
builds its outgoing options by copying exactly those. Any other key reaches no branch
and is dropped — no throw, no warning. QueryParams also carries an index signature, so
the type system accepts both spellings equally.

The consequence is an unbounded read, not a truncated one: the platform's GET list
route has no default page size (objectstack packages/client/src/index.ts, pinned in
its client.test.ts), so an absent top returns the entire match set. A dropped cap is
therefore invisible until the object is large.

The four live sites

file call dropped key
apps/console/src/sdui-workbench-preview.tsx:65 find(objectName, { top: 200 }) top
packages/app-shell/src/views/ObjectView.tsx:1561 find(…, { limit: 0 }) limit
packages/app-shell/src/views/metadata-admin/AssignedUsersSection.tsx:109 find('sys_permission_set', { $filter: {…}, limit: 1 }) limit
content/docs/guide/react-pages.md:129 find(…, { filters: [['status','=','open']] }) filters

ObjectView.tsx:1561's limit: 0 is worth reading twice: $top: 0 is honoured end to
end as "no records" (objectstack #6485 pinned it), so if that call means what it says,
dropping the key inverts it from no rows to every row.

AssignedUsersSection.tsx:109 is a one-line-away neighbour of three correct calls
($top: 500, $top: 200, $top: 1000 on L124/131/136) — the shape a reviewer's eye
slides over.

sdui-workbench-preview.tsx:66 also reads all.records off the result, with no .data
arm. find() resolves to a QueryResult (data, never records), so that preview
lists nothing regardless of the cap. skills/objectui/guides/data-integration.md:268
publishes the same return result.records; to skill consumers.

Why a rule, and which one

eslint-rules/no-query-params-under-options.js already gates the sibling shape —
find(obj, { options: { $top: 100 } }) — and its own header makes the argument for
mechanising this family: the mistake type-checks, it publishes, the symptom looks like a
data problem rather than a code problem, "so a review catches it once and then misses the
next one." It cites two live instances by two authors (object-timeline,
objectui#4009 / objectstack#7137; object-kanban, objectui#4025).

The rule bans a $-prefixed key under options. It does not look at a bare key at
the top level, which is the other half of the same class — and the half with four live
sites. Extending it (or adding a sibling rule) is the cheap move: the AST walk already
lands on the params object literal of a find/findOne call, the fix is a second
predicate, and the contract lives in the same repo as the rule.

Suggested shape, narrow on purpose: flag a static key that is a known query-option
name without its $ (top, skip, filter, filters, select, orderby, sort,
expand, search, count, limit, offset) in the second argument of an
adapter.find / dataSource.find / …findOne call. Not "any unprefixed key" — adapters
legitimately take adapter-specific params, which is why the index signature exists.

An objectstack-side gate was considered for #10288 and rejected: QueryParams is
objectui's contract, so a checker there would have to hard-code another repo's key list
and become a second source of truth for it. The rule belongs next to the type it
enforces.

Related

  • objectstack#10288 — the same two defects in examples/app-showcase, fixed there.
  • objectstack#10469 — the same .records misread in objectstack's own react-pages guide.
  • objectui#4734 / objectstack#7147 — the options: { $top } half, already gated.

Activity

  1. added
    repo:objectuiSeam card: cross-repo ordering with objectui is the substance (pure objectui fixes live in objectui)
    on Aug 21, 2026
  2. os-zhuang commented on Aug 21, 2026

    @os-zhuang
    ContributorAuthor

    Triage: moved to objectstack-ai/objectui#5458 (file-at-destination rule — every named site and the lint rule that should gate them live in objectui; transfer rebuilt with provenance header and qualified refs, queued there as Bug). Closing here as moved; this card carries no objectstack-side work.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    repo:objectuiSeam card: cross-repo ordering with objectui is the substance (pure objectui fixes live in objectui)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions