…plate
Collapse three repeated daemon-handler patterns into shared helpers in
src/daemon/handlers/response.ts and handler-utils.ts:
- requireCommandSupported(command, device, { message?, hint? }) replaces ~21
verbatim isCommandSupportedOnDevice UNSUPPORTED_OPERATION guards. The richest
variant (generic dispatch) folds the unsupportedHintForDevice hint behind the
`hint` option; custom-message sites pass `message`.
- noActiveSessionError() / NO_ACTIVE_SESSION_MESSAGE replace the duplicated
'No active session. Run open first.' SESSION_NOT_FOUND literal across handlers
(and the inline router/AppError variants).
- recordSessionAction gains optional positionals/flags overrides so ~15 inline
sessionStore.recordAction({ command, positionals, flags, result }) literals
(find, record-trace, record-trace-recording, session, session-close,
session-perf-xctrace, install-source) route through the one wrapper.
behaviorless: every site keeps its exact error code/message (and hint where
present) and recorded action shape; helpers reproduce each variant.
What
Collapses three repeated daemon-handler boilerplate patterns into shared helpers. Pure-deletion dedup, no behavior change.
1. Capability guard ->
requireCommandSupported(command, device, { message?, hint? })The verbatim
if (!isCommandSupportedOnDevice(cmd, device)) return errorResponse('UNSUPPORTED_OPERATION', '<cmd> is not supported on this device')block recurred ~21x. New helper (next toerrorResponseinsrc/daemon/handlers/response.ts) returns the failure response ornull:request-generic-dispatch.ts) also calledunsupportedHintForDevice— that hint is folded in behind ahint: trueoption, so that site keeps its hint and every other site stays identical (nohintkey).react-native,session-stateshutdown,install-source,snapshot-runtimediff/snapshot) passmessageto preserve their exact text.2. No-active-session literal ->
noActiveSessionError()/NO_ACTIVE_SESSION_MESSAGEThe
'No active session. Run open first.'SESSION_NOT_FOUNDliteral was duplicated across ~9 sites (handlers usingerrorResponse, the inlinerequest-routerfinalize object, and theAppErrorthrow ininteraction-runtime). All now reference the single builder/constant. Theif (!session) return ...guard structure is preserved so TypeScript narrowing is unchanged.3. recordAction literal -> existing
recordSessionActionwrapperThe inline
sessionStore.recordAction(session, { command, positionals, flags, result })object literal was constructed at ~15 routable sites.recordSessionAction(handler-utils.ts, previously 1 caller) gained optional{ positionals?, flags? }overrides to cover the variants (resolved positionals, strippedpublicFlags, hardcoded[]). Routed:find(6 sites +recordFindAction),record-trace(2),record-trace-recording(2),session(2),session-close,session-perf-xctrace,install-source.Sites intentionally left as-is (would change recorded shape or have no
reqin scope):session-open(carries aruntimefield),request-generic-dispatch/interaction-commonrecorders (noreqparam), and the pre-existingrecordIfSession/recordSnapshotRuntimeActionlocal wrappers.LOC dropped
Net -56 (193 insertions / 249 deletions across 24 files, including the +~43 lines of new shared helpers).
Validation
tsc -p tsconfig.json --noEmit— cleanoxfmt --write+oxlint --deny-warningson all changed files — cleanvitest run daemon— 901/902 pass; the single failure (daemon-client > downloadRemoteArtifact removes partial files after mid-stream aborts) is unrelated network/abort-timing flakiness (untouched file) and passes in isolation.behaviorless
Every site keeps its exact error
code/message(andhintwhere present) and recorded action shape:requireCommandSupported's default message is\${command} is not supported on this device`(verifiedPUBLIC_COMMANDS.clipboard/.typeequal their literals);message/hintoptions reproduce the non-default variants;noActiveSessionError()returns the identical{ ok: false, error: { code, message } };recordSessionAction` overrides reproduce each site's positionals/flags exactly.