Skip to content

app-shell metadata-admin inspectors escape to the real network via useObjectFields/useObjectOptions (ECONNREFUSED noise, different probe than #4688) #4697

Description

@yinlianghui

Split off objectui#4688 while implementing it. That card's own acceptance criteria anticipated this: "a remainder from a DIFFERENT probe is a new finding, not this card's" — measurement below shows the entire app-shell + plugin-designer 180-line ECONNREFUSED count objectui#4688 attributed to the grid's record-level explain probe is actually 100% a different, pre-existing probe, unrelated to ObjectGrid/useRecordCrudVerdicts/#4296.

Mechanism

packages/app-shell/src/views/metadata-admin/previews/useObjectFields.ts and useObjectOptions.ts both call useMetadataClient() (../useMetadata) with no override, and both fire their fetch unconditionally in a mount-time useEffect — no click, no host adapter required. useMetadataClient() returns createConsoleMetadataClient(...), which defaults its transport to createAuthenticatedFetch() (@object-ui/auth), which in turn calls the bare global fetch. Under happy-dom with no server listening, that is a REAL request to the default origin, exactly the same failure shape as objectui#3339 / objectui#4688 (connect ECONNREFUSED 127.0.0.1:3000) but a different endpoint (GET /api/v1/meta/object and GET /api/v1/meta/object/<name> — confirmed by instrumenting the fetch call site directly) — not POST /api/v1/security/explain.

Both hooks document graceful degradation on failure ("a missing object name, a 404, or a transport error all resolve to an empty list with error set"), so the failing calls do not fail any test — same "harmless noise" character as #4688's escape, just a different call site.

Five inspector components reach these hooks directly or via ConditionBuilder:

  • ConditionBuilder.tsxuseObjectFields(objectName) (no override)
  • PageBlockInspector.tsx → both useObjectFields(objectName) and useObjectOptions()
  • HookDefaultInspector.tsxuseObjectOptions() (+ renders ConditionBuilder)
  • ActionDefaultInspector.tsxuseObjectFields(objectName)
  • ViewVariantInspector.tsxuseObjectFields(...)

Measured (current main, b4bccc794)

Per-file ECONNREFUSED line counts, pnpm exec vitest run <file> --maxWorkers=1 each (isolated runs — a batched run undercounts because some in-flight rejections do not settle before the process exits):

fileECONNREFUSED lines
packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.i18n.test.tsx70
packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.sectionName.test.tsx40
packages/app-shell/src/views/metadata-admin/inspectors/ConditionBuilder.celGate.test.tsx12
packages/app-shell/src/views/metadata-admin/inspectors/HookDefaultInspector.condition.test.tsx12
packages/app-shell/src/views/metadata-admin/inspectors/ViewVariantInspector.celGate.test.tsx12
packages/app-shell/src/views/metadata-admin/inspectors/ViewVariantInspector.homeGate.test.tsx12
packages/app-shell/src/views/metadata-admin/inspectors/ActionDefaultInspector.celGate.test.tsx8
packages/app-shell/src/views/metadata-admin/inspectors/ConditionBuilder.test.tsx8
packages/app-shell/src/views/metadata-admin/inspectors/HookDefaultInspector.celGate.test.tsx6

Sum = 180, exactly matching objectui#4688's "app-shell + plugin-designer: 180" row when the full packages/app-shell/src/views/metadata-admin/inspectors/ directory is run together. Bisection confirmed no other directory in app-shell or in plugin-designer (which contributes 0) is involved: views/ top-level files → 0, views/studio-design/ → 0, views/metadata-admin/previews/ → 0, views/metadata-admin/inspectors/ → 180.

Diagnostic proof of the URL (instrumented globalThis.fetch via vi.stubGlobal inside vitest.setup.base.ts, output routed through process.stderr.write since console.error is swallowed by vitest for passing tests):

OS_DIAG_FETCH_URL /api/v1/meta/object
OS_DIAG_FETCH_URL /api/v1/meta/object/contact

Suggested direction

Same class of remedy as #3339/#4105/#4688 — a recorded double, not a global error sink — but for a different hook. packages/app-shell/src/views/metadata-admin/inspectors/FlowReferenceField.lookup.test.tsx already has the precedent shape for this exact package: vi.mock('../useMetadata', () => ({ useMetadataClient: () => stubClient })), where stubClient is a minimal { list: vi.fn(async () => []) , get: vi.fn(async () => null) } (or wire a real MetadataClient over a stub fetch, matching MetadataFieldsPage.retiredIndexed.test.tsx's pattern). Either keeps existing assertions passing unchanged, since none of the 9 files' assertions read the fetched object list/fields.

Provenance

Measured in worktree objectui-issue-4688 (branch claude/issue-4688-consumer-explain-doubles) while implementing objectui#4688, on top of b4bccc794.


Generated by Claude Code

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions