Project: doctolib-doctolib · Branch: .invalid · Mode: ` · **Model:**claude-opus-4-7`
Stats: 1 user messages, 29 tool calls, tools used: Bash, Read, Write.
Conversation
Turn 1 — User
Simplify and refine code changed in PR https://github.com/doctolib/doctolib/pull/240721 on branch fhir-2215_wire-dmp-errors-through-dmpnhradapter in /Users/USER/workspace/doctolib/doctolib.
The changed files to focus on are:
- packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.ts
- packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.test.ts
- packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.ts
- packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.test.ts
- packages/@doctolib/national-health-records/src/variants/types.ts
- packages/@doctolib/national-health-records/src/variants/dmp/useDmpDocumentListVariant.ts
Key context:
- The PR implements error-as-data propagation: DmpNhrAdapter.searchDocuments returns { status: 'error', message, code } instead of throwing
- useSearchDocuments wraps queryClient.fetchQuery in a try/catch and checks result.status === 'error'
- TypeScript: project uses strict settings, no any, no eslint-disable comments
- Tests use Jest with @testing-library/react renderHook and React Query v5
After simplifying, run tests to verify nothing broke:
npx jest --config packages/@doctolib/national-health-records/jest.config.cjs --testPathPattern="(useSearchDocuments|DmpNhrAdapter).test.ts" --no-coverage
Also run ESLint and prettier checks:
npx eslint packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.ts packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.ts
npx prettier --check packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.ts packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.ts
Only simplify recently modified code. Preserve all functionality. Do not add comments unless the WHY is non-obvious.
Turn 1 — Assistant
I'll analyze the PR changes and simplify the code while preserving functionality. Let me start by examining the files.
Tools used:
Bash:git status && git log --oneline -5
Tool results:
→ result (ok): On branch fhir-2215_wire-dmp-errors-through-dmpnhradapter Your branch is up to date with 'origin/fhir-2215_wire-dmp-errors-through-dmpnhradapter'.
It took 2.57 seconds to enumerate untracked files. See 'git help status' for information on how to improve this.
nothing to commit, working tree clean c6ce60251b7c test(FHIR-2215): add coverage for undefined apiErrorCode and non-Error thrown value 4ef99f36349c refactor(FHIR-2215): convert useSearchDocuments to named export, use instanceof Error in …(truncated, 297 more chars)
I'll follow plan mode - I'll analyze the files and create a plan without making edits.
Tools used:
Read:/Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.ts
Tool results:
→ result (ok): 1 import type { 2 DmpApi, 3 DmpAuthor, 4 DmpDocument, 5 DmpDocumentContent, 6 DocumentStatusUpdateProps, 7 SearchProps, 8 SearchResult as DmpSearchResult, 9 } from '@doctolib/dmp' 10 import { DmpApiError, DOCUMENT_STATUS, DOCUMENT_VISIBILITY, getPatientInsForDMP } from '@doctolib/dmp' 11 import { isUnstructured } from '@doctolib/cda/types/UnstructuredBody' 12 import { isStructured } from '@doctolib/cda/types/StructuredBody' 13 import type { Patient } from 'monolith_DEPRECATED/types …(truncated, 9256 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.test.ts
Tool results:
→ result (ok): 1 import { getPatientInsForDMP } from '@doctolib/dmp'
2 import type { DmpApi, DmpApiErrorInfo, DmpDocumentContent, SearchResult as DmpSearchResult } from '@doctolib/dmp'
3 import type { NhrDocumentSearchProps } from '@doctolib/tpf-national-health-records'
4 import { DmpNhrAdapter } from './DmpNhrAdapter'
5
6 jest.mock('@doctolib/dmp', () => {
7 const actual = jest.requireActual<typeof import('@doctolib/dmp')>('@doctolib/dmp')
8 return {
9 ...actual,
10 getPatientInsForDMP: jest.fn()
…(truncated, 22513 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.ts
Tool results:
→ result (ok): 1 import { useQueryClient } from '@tanstack/react-query-v5' 2 import type { Patient } from 'monolith_DEPRECATED/types/doctor/api/Patient' 3 import { isFeatureEnabled } from '@doctolib/variation_management' 4 import type { NhrDocumentListVariant, MapFiltersToSearchDocumentsParams, ServerFilters } from '../variants/types' 5 import { useNationalHealthRecords } from './useNationalHealthRecords' 6 import { generateNhrDocuments } from '../mocks/generateNhrDocuments' 7 import { filterMockedDocuments } …(truncated, 2493 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.test.ts
Tool results:
→ result (ok): 1 import React from 'react' 2 import { renderHook } from '@testing-library/react' 3 import { QueryClient, QueryClientProvider } from '@tanstack/react-query-v5' 4 import * as variationManagement from '@doctolib/variation_management' 5 import type { Patient } from 'monolith_DEPRECATED/types/doctor/api/Patient' 6 import type { NhrDocument, NhrDocumentSearchParams } from '@doctolib/tpf-national-health-records' 7 import { useNationalHealthRecords } from './useNationalHealthRecords' 8 import { generat …(truncated, 16501 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/variants/types.ts
Tool results:
→ result (ok): 1 import type { ColumnConfig, FilterConfig, SearchDocumentsResult } from '@doctolib/tpf-national-health-records-ui'
2 import type { Patient } from 'monolith_DEPRECATED/types/doctor/api/Patient'
3 import type { NhrDocumentSearchParams } from '@doctolib/tpf-national-health-records'
4
5 export type { SearchDocumentsResult }
6
7 export type NhrDocumentListVariant = {
8 columns: ColumnConfig[]
9 filters: FilterConfig[]
10 searchDocuments: (appliedFilters?: Record<string, unknown>) => Promise<
…(truncated, 820 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/variants/dmp/useDmpDocumentListVariant.ts
Tool results:
→ result (ok): 1 import { columnsFactory } from './factories/columnsFactory'
2 import { filtersFactory } from './factories/filtersFactory'
3 import type { NhrDocumentListVariant, DocumentSearchOptions } from '../types'
4 import { useSearchDocuments } from '../../hooks/useSearchDocuments'
5 import { mapFiltersToSearchDocumentsParams } from './mappers/mapFiltersToSearchDocumentsParams'
6
7 export const useDmpDocumentListVariant = (patient: DocumentSearchOptions['patient']): NhrDocumentListVariant => {
8 const
…(truncated, 205 more chars)
Now let me check the project's ESLint config and conventions regarding function keyword vs arrow functions.
Tools used:
Bash:find /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records -name ".eslintrc*" -maxdepth 3 2>/dev/null; find /Users/USER/workspace/doctolib/doctolib/packages/@doc …(truncated, 72 more chars)
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/node_modules/react-i18next/.eslintrc.json /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/jest.config.cjs /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/babel.config.cjs
Tools used:
Bash:ls /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/hooks/ /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/busine …(truncated, 12 more chars)
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/business/adapters/: DmpNhrAdapter.test.ts DmpNhrAdapter.ts parseHl7DateToIso.test.ts parseHl7DateToIso.ts
/Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/hooks/: useDmpNhr.test.ts useDmpNhr.ts useGetDocumentsContent.test.ts useGetDocumentsContent.ts useNationalHealthRecords.test.ts useNationalHealthRecords.ts useSearchDocuments.test.ts useSearchDocuments.ts
Tools used:
Read:/Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/hooks/useNationalHealthRecords.ts
Tool results:
→ result (ok): 1 import type {
2 NhrDocumentSearchProps,
3 NhrDocumentSearchResult,
4 NhrGetDocumentsContentProps,
5 NhrDocumentContentResult,
6 NhrDocumentMetadataUpdateProps,
7 NhrDocumentMetadataUpdateResult,
8 } from '@doctolib/tpf-national-health-records'
9 import { useProductLineVariation, ProductLineKey } from '@doctolib/variation_management'
10 import type { Patient } from 'monolith_DEPRECATED/types/doctor/api/Patient'
11 import { useDmpNhr } from './useDmpNhr'
12
13 export type UseNhrOper
…(truncated, 966 more chars)
Good - confirming that arrow functions are the convention in this package. Now let me look at the existing types for NhrDocumentSearchResult to understand what fields are expected.
Tools used:
Bash:find /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/tpf-national-health-records -type d -maxdepth 3 2>/dev/null | head
Tool results:
→ result (ok): (Bash completed with no output)
Tools used:
Bash:grep -r "NhrDocumentSearchResult" /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/ -l 2>/dev/null | head
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.ts /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/hooks/useNationalHealthRecords.ts /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.test.ts /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/shared_patient_file_and_appointment_page_components/node_modules/@docto …(truncated, 1222 more chars)
Tools used:
Bash:grep -A 20 "NhrDocumentSearchResult" /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/shared_patient_file_and_appointment_page_components/node_modules/@doctolib/tpf-national-health-reco …(truncated, 40 more chars)
Tool results:
→ result (ok): export type NhrDocumentSearchResult = { documents: NhrDocument[] }
// Get Document Content export type NhrGetDocumentsContentParams = { ids: string[] }
export type NhrGetDocumentsContentProps = { patient?: NhrPatientInformation params: NhrGetDocumentsContentParams }
export type NhrDocumentContentResult<TCategorization = unknown, TMetadata = unknown> = { files: NhrDocumentFile[] categorization: TCategorization metadata: TMetadata }
// Update Metadata
Interesting - the type in tpf-national-health-records is just { documents: NhrDocument[] }. The status/message/code fields are added via this PR via union or extension. Let me check the actual search of status & message extensions.
Tools used:
Bash:grep -rn "status.*success\|status.*error\|SearchResult" /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/shared_patient_file_and_appointment_page_components/node_modules/@doctolib/tpf-n …(truncated, 38 more chars)
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/shared_patient_file_and_appointment_page_components/node_modules/@doctolib/tpf-national-health-records/src/index.ts:8: NhrDocumentSearchResult, /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/shared_patient_file_and_appointment_page_components/node_modules/@doctolib/tpf-national-health-records/src/business/types/api.ts:28:export type NhrDocumentSearchResult = { /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/shared …(truncated, 1482 more chars)
Tools used:
Bash:grep -rn "NhrDocumentSearchResult\|status.*NhrResultStatus" /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/shared_patient_file_and_appointment_page_components/node_modules/@doctolib/t …(truncated, 52 more chars)
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/shared_patient_file_and_appointment_page_components/node_modules/@doctolib/tpf-national-health-records/src/business/types/api.ts:7: status: NhrResultStatus /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/shared_patient_file_and_appointment_page_components/node_modules/@doctolib/tpf-national-health-records/src/business/types/api.ts:28:export type NhrDocumentSearchResult = {
Tools used:
Read:/Users/USER/workspace/doctolib/doctolib/packages/@doctolib/shared_patient_file_and_appointment_page_components/node_modules/@doctolib/tpf-national-health-records/src/business/types/api.ts
Tool results:
→ result (ok): 1 import type { DateRange, NhrDocument, NhrDocumentFile } from '#business/types/document'
2 import type { NhrPatientInformation } from '#business/types/participant'
3
4 export type NhrResultStatus = 'success' | 'skipped' | 'error' | 'partial success'
5
6 type NhrResult = {
7 status: NhrResultStatus
8 message?: string
9 }
10
11 // Document Search
12 export type NhrDocumentSearchParams = {
13 formats?: string[]
14 categories?: string[]
15 status?: string
16 serviceStartDate?: DateRa
…(truncated, 1920 more chars)
The NhrDocumentSearchResult is { documents: NhrDocument[] } but the adapter returns objects with status, message, code fields. TypeScript would allow this because NhrDocumentSearchResult doesn't have strict exact matching. Let me check how it's typed in the adapter return:
Let me also check SearchDocumentsResult from the variant types.
Tools used:
Bash:grep -rn "SearchDocumentsResult\b" /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/ 2>/dev/null | grep -v node_modules/react-i18next | head
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/variants/types.ts:1:import type { ColumnConfig, FilterConfig, SearchDocumentsResult } from '@doctolib/tpf-national-health-records-ui' /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/variants/types.ts:5:export type { SearchDocumentsResult } /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/variants/types.ts:10: searchDocuments: (applie …(truncated, 69 more chars)
Tools used:
Bash:grep -rn "SearchDocumentsResult\b" /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/node_modules/@doctolib/tpf-national-health-records-ui/ 2>/dev/null | head -5
Tool results:
→ result (ok): (Bash completed with no output)
Tools used:
Bash:find /Users/USER/workspace/doctolib/doctolib -name "tpf-national-health-records-ui" -type d 2>/dev/null | head -5
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/doctolib/.yalc/@doctolib/tpf-national-health-records-ui /Users/USER/workspace/doctolib/doctolib/node_modules/@doctolib/tpf-national-health-records-ui /Users/USER/workspace/doctolib/doctolib/packages/@doctolib/shared_patient_file_and_appointment_page_components/node_modules/@doctolib/tpf-national-health-records-ui /Users/USER/workspace/doctolib/doctolib/.claude/worktrees/fhir-2144_nhr-document-list-add-action-config-column/packages/@doctolib/national-health-records/ …(truncated, 289 more chars)
Tools used:
Bash:grep -rn "SearchDocumentsResult\|searchDocumentsResult" /Users/USER/workspace/doctolib/doctolib/node_modules/@doctolib/tpf-national-health-records-ui/src/ 2>/dev/null | head -20
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/doctolib/node_modules/@doctolib/tpf-national-health-records-ui/src/index.ts:18: SearchDocumentsResult, /Users/USER/workspace/doctolib/doctolib/node_modules/@doctolib/tpf-national-health-records-ui/src/types.ts:58:export type SearchDocumentsResult = { /Users/USER/workspace/doctolib/doctolib/node_modules/@doctolib/tpf-national-health-records-ui/src/types.ts:66:export type SearchDocumentsFn = (appliedFilters?: Record<string, unknown>) => Promise<SearchDocumentsResult …(truncated, 1 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/doctolib/node_modules/@doctolib/tpf-national-health-records-ui/src/types.ts(50–80)
Tool results:
→ result (ok): 50 render: (
51 /* generic — DMP variant casts to DmpServerFilters /
52 filters: Record<string, unknown>,
53 onChange: (patch: Record<string, unknown>) => void,
54 ctx: FilterRenderContext
55 ) => ReactNode
56 }
57
58 export type SearchDocumentsResult = {
59 data: NhrDocument[]
60 total?: number
61 status: NhrResultStatus
62 message?: string
63 code?: string
64 }
65
66 export type SearchDocumentsFn = (appliedFilters?: Record<string, unknown>) => Promise<SearchDo
…(truncated, 478 more chars)
Now I have a complete picture. Let me check the NhrDocumentSearchResult type — to see what fields we can rely on for message/code. It returns { documents, status, message, code } from the adapter which will be type-widened. Let me check the diff to confirm what was recently changed vs pre-existing code:
Tools used:
Bash:git log --oneline origin/master..HEAD 2>/dev/null; git diff origin/master...HEAD --stat 2>/dev/null | head -30
Tool results:
→ result (ok): c6ce60251b7c test(FHIR-2215): add coverage for undefined apiErrorCode and non-Error thrown value 4ef99f36349c refactor(FHIR-2215): convert useSearchDocuments to named export, use instanceof Error in retry guard 3300dbbdd290 feat(FHIR-2215): wire DMP errors through DmpNhrAdapter and useSearchDocuments package-lock.json | 176 ++++++++++++++++++--- package.json | 2 +- .../@doctolib/national-health-records/package.json | 2 …(truncated, 508 more chars)
Tools used:
Bash:git diff origin/master...HEAD -- packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.ts
Tool results:
→ result (ok): diff --git a/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.ts b/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.ts index 8dd3bbcea9f8..d8222903a832 100644 --- a/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.ts +++ b/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.ts @@ -7,7 +7,7 @@ import type { SearchProps, SearchResult as DmpSearchResult, } from '@doctolib/dmp' …(truncated, 1663 more chars)
Tools used:
Bash:git diff origin/master...HEAD -- packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.ts
Tool results:
→ result (ok): diff --git a/packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.ts b/packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.ts index c3b3f77ca0bc..d5eacee01d2b 100644 --- a/packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.ts +++ b/packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.ts @@ -15,18 +15,10 @@ interface UseSearchDocumentsProps { mapFiltersToSearchDocumentsParams: MapFiltersToSearchDocumentsParams }
…(truncated, 3390 more chars)
Tools used:
Bash:git diff origin/master...HEAD -- packages/@doctolib/national-health-records/src/variants/types.ts packages/@doctolib/national-health-records/src/variants/dmp/useDmpDocumentListVariant.ts
Tool results:
→ result (ok): diff --git a/packages/@doctolib/national-health-records/src/variants/dmp/useDmpDocumentListVariant.ts b/packages/@doctolib/national-health-records/src/variants/dmp/useDmpDocumentListVariant.ts index 12f5f00efa6d..d8311dd47f40 100644 --- a/packages/@doctolib/national-health-records/src/variants/dmp/useDmpDocumentListVariant.ts +++ b/packages/@doctolib/national-health-records/src/variants/dmp/useDmpDocumentListVariant.ts @@ -1,11 +1,11 @@ import { columnsFactory } from './factories/columnsFactory …(truncated, 1760 more chars)
Tools used:
Bash:git diff origin/master...HEAD -- packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.test.ts
Tool results:
→ result (ok): diff --git a/packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.test.ts b/packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.test.ts index f22a0aee4b1f..b88769c2544a 100644 --- a/packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.test.ts +++ b/packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.test.ts @@ -7,7 +7,7 @@ import type { NhrDocument, NhrDocumentSearchParams } from '@doctolib/tpf-nationa import { useNat …(truncated, 8341 more chars)
Tools used:
Bash:git diff origin/master...HEAD -- packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.test.ts | head -200
Tool results:
→ result (ok): diff --git a/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.test.ts b/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.test.ts index c618b88b07f9..6ac82fdfff9e 100644 --- a/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.test.ts +++ b/packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.test.ts @@ -1,20 +1,24 @@ import { getPatientInsForDMP } from '@doctolib/dmp' -import type { …(truncated, 4051 more chars)
I have a complete picture now. Let me read the plan file location to set up a plan.
Now let me analyze and create a simplification plan. Here are the opportunities I see:
Analysis of code changes:
1. DmpNhrAdapter.ts (lines 159-177)
The searchDocuments method has three exit paths: missing INS error, API error, success. The flow is already clean. But there's a minor opportunity to inline apiSearchProps since it's used once:
const apiResponse = await this.dmpApi.search(toSearchProps(props))
This removes an unnecessary local variable. Though it's a minor change and arguably makes it less readable, so best left alone.
No significant simplification is warranted here — the code is already clean and the explicit return shape documents the error variants clearly.
2. useSearchDocuments.ts
This has more opportunity:
-
Two consecutive early returns for
!insand!nhrOperationsreturning identical values. Could be combined with||but splitting is currently more readable and debug-friendly. Each carries a different semantic check — combining is fine since the return value is identical and arguably cleaner. -
Consider extracting the error result factory — but only if there's meaningful gain. I count 3 error-result shapes and 4 success-result shapes. Extracting would help consistency. However, since the shapes vary slightly (with/without
message,code,data), a factory risks obscuring intent. -
The
?? nullaftersearchDocumentsis unusual. The adapter's return type isNhrDocumentSearchResult, which is never undefined. The?? nullis defensive — original code hadresult?.documents ?? []. The new shape handles undefined explicitly withif (!result). The?? nullserves as a normalizer for React Query caching, but since the type says it's always a result, the?? nullis dead code TypeScript-wise. However, the subsequentif (!result)suggests the author expects possible undefined at runtime. This is safe defensive code but adds noise. We could remove both the?? nulland the!resultcheck if the type is truthful. Verify with the mock in tests: -
Test "returns an empty array with total 0 when nhrOperations resolves to undefined" — this tests
mockSearchDocuments.mockResolvedValue(undefined)— so the test exercises the defensive path. Behavior preservation requires keeping it. -
The retry guard uses
error.message.includes(...)withinstanceof Errorcheck — clean as-is.
3. variants/types.ts
Already clean. The export type { SearchDocumentsResult } is idiomatic re-export.
4. useDmpDocumentListVariant.ts
Just updates the import. Already clean.
5. Test files
Both test files are essentially mechanical updates to match the new types. I see some minor opportunities:
-
In
useSearchDocuments.test.ts,as constonstatus: 'success'is repeated many times. Could be extracted to a helper — but tests value explicitness over DRY. -
createWrapperhas comments that explain WHY — good to keep.
Let me think about additional simplifications:
Potential simplification in useSearchDocuments.ts:
The two early-return lines for !ins and !nhrOperations could be merged:
if (!ins || !nhrOperations) return { data: [], total: 0, status: 'success' }
This is a reasonable simplification — same semantics, one line instead of two, not harder to debug.
Potential simplification in error shape:
The catch block returns a shape without code. The error-from-adapter path returns one with code. The type SearchDocumentsResult has code? optional. This is consistent.
The queryFn ?? null:
queryFn: async () =>
(await nhrOperations.searchDocuments({...})) ?? null,
This ?? null is defensive. React Query requires queryFn to not return undefined. This is a legitimate concern for React Query v5 which throws if queryFn returns undefined. So ?? null is required, not a simplification candidate.
Let me also verify there's a worktrees convention check for this branch:
Tools used:
Bash:git branch --show-current && pwd
Tool results:
→ result (ok): fhir-2215_wire-dmp-errors-through-dmpnhradapter /Users/USER/workspace/doctolib/doctolib
OK, we're on the feature branch directly (not worktree). Let me now write the plan.
Tools used:
Write:/Users/USER/.claude/plans/vast-weaving-stearns-agent-a432121bfe6e72916.md(5530 chars)
# Simplification plan — FHIR-2215 DMP error wiring PR
Branch: `fhir-2215_wire-dmp-errors-through-dmpnhradapter`
Scope: files changed vs `origin/master` in PR #240721.
…(truncated, 82 more lines)
Tool results:
→ result (ok): File created successfully at: /Users/USER/.claude/plans/vast-weaving-stearns-agent-a432121bfe6e72916.md
Here is the plan summary.
Plan
I reviewed all six files changed in PR #240721 on branch fhir-2215_wire-dmp-errors-through-dmpnhradapter. The code is already in good shape — the error-as-data migration is consistent and the tests mirror the new contract cleanly. I identified one small simplification worth making; everything else is already at the right level.
Single planned edit
/Users/USER/workspace/doctolib/doctolib/packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.ts — lines 32-34:
Merge the two consecutive early-return guards (both returning the identical value) into one combined check:
// before
const { ins } = patient
if (!ins) return { data: [], total: 0, status: 'success' }
if (!nhrOperations) return { data: [], total: 0, status: 'success' }
// after
const { ins } = patient
if (!ins || !nhrOperations) return { data: [], total: 0, status: 'success' }
Behavior is identical because both guards return the same literal. Tests cover both branches independently and will still pass.
Intentionally not changed
DmpNhrAdapter.ts— three explicit exit paths (missing-INS error,apiResponse.error, success) each with distinct shapes. Factoring would obscure intent.useSearchDocuments.ts?? null+if (!result)— required at runtime. React Query v5 throws whenqueryFnreturnsundefined, and the "resolves to undefined" test exercises this path. The?? nullnormalizes for the cache, and the!resultcheck handles the normalized case before touching.status/.documents.- try/catch around
fetchQuery— idiomatic for the surrounding async flow; converting to.catch()would fragment the happy path. - Repeated
status: 'success' as constin the test — explicit literals are more readable per-test than asearchSuccess(...)helper would be. - Repeated
renderHook(..., { wrapper: createWrapper() })pattern — pre-existing, out of PR scope. DmpNhrAdapter.test.tsmock rewrite to userequireActual— necessary so the realDmpApiErrorclass is available in the mock; not simplifiable further.variants/types.ts— the re-export ofSearchDocumentsResultfrom@doctolib/tpf-national-health-records-uiis idiomatic and already minimal.useDmpDocumentListVariant.ts— just the import form change, already clean.
Verification after the edit
npx jest --config packages/@doctolib/national-health-records/jest.config.cjs --testPathPattern="(useSearchDocuments|DmpNhrAdapter).test.ts" --no-coveragenpx eslint packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.ts packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.tsnpx prettier --check packages/@doctolib/national-health-records/src/hooks/useSearchDocuments.ts packages/@doctolib/national-health-records/src/business/adapters/DmpNhrAdapter.ts
Plan file: /Users/USER/.claude/plans/vast-weaving-stearns-agent-a432121bfe6e72916.md
Exit plan mode to apply the change, or tell me if you want a different scope.