Skip to content

A SIXTH client-SDK erasure spelling, larger than the fifth: 46 unannotated methods return res.json() directly, so their published type is any with neither Promise nor unwrapResponse anywhere in the text #12037

Description

@os-zhuang

Found while implementing #11925 and out of its declared scope — that card's population is defined as
"no return annotation, typed from unwrapResponse< …any… >", and these methods never call unwrapResponse
at all. Recorded because it is larger than the fifth spelling and invisible to every grep used so far.

Measured at c804f0ca5, in packages/client/src/index.ts.

The lineage

#8140's census enumerated four spellings, all putting the any inside a visible Promise< … > annotation.
#11925 found a fifth: no annotation at all, the type inferred from unwrapResponse< …any… >. Both are
text-visible — one greps Promise, the other greps unwrapResponse.

This sixth writes neither:

query: async(payload: any)=>{constroute=this.getRoute('analytics');constres=awaitthis.fetch(`${this.baseUrl}${route}/query`,{method: 'POST',body: JSON.stringify(payload)});returnres.json();},

Response.json() is Promise<any> in the DOM lib, so the method's published type is any — with no
Promise, no unwrapResponse, and no any token anywhere in the method body.

The count, and how the three populations relate

A text census cannot see this class, so the ground truth was taken from the TypeScript checker instead —
walking the public members of ObjectStackClient / ScopedProjectClient and resolving each awaited return
type:

reachable methods walked : 273
…whose awaited return contains `any`: 89
of those, ANNOTATED : 4
of those, UNANNOTATED : 85

Against the spelling-based census of #11925's population:

spelling-census unannotated methods (unwrapResponse<..any..>): 39
tsc-truth unannotated methods with `any` in published return : 85
spelling ⊆ tsc-truth? missing from tsc: []
tsc-truth NOT in spelling population : 46

The 39 are a strict subset. The residual 46 are this spelling:

$ grep -cE '^\s*return res\.json\(\);\s*$' packages/client/src/index.ts
48
# reverse-check — a term independently present, never a substring of the term under test
$ grep -cE 'this\.getRoute\(' packages/client/src/index.ts
186

(48 textual sites, 46 of which sit in unannotated methods the checker reports as any.)

Which families

analytics.* (query, meta, explain, queryDataset), organizations.* including invitations.* and
teams.* (~24), oauth.applications.* and oauth.consent (6), auth.* including sessions.*,
twoFactor.* and accounts.* (~14), and automation.trigger.

Most of these are better-auth relays (this.getRoute('auth')), which is likely why they bypassed
unwrapResponse — better-auth does not send the { success, data } envelope, so there is nothing to strip.
That makes it a coherent family rather than 46 scattered oversights, and it means the binding question for
them is "what does better-auth declare", not "which @objectstack/spec type".

Why it is worth its own card

Related: #8140, #11925, #11924.


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions